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 03.6"

From OWASP
Jump to: navigation, search
(add content)
Line 11: Line 11:
 
=== Strategy ===
 
=== Strategy ===
  
This WebGoat lesson shows a money transfer page with the user's balance, the recipient's account ID and the amount of money to transfer. The application uses AJAX to submit the transaction. The vulnerability is that malicious code injected into the page can call the AJAX code directly - bypassing client side validation - which results in a silent transaction without the user's authorization.
+
This WebGoat lesson, a schedule and airfare for a roundtrip flight from Boston (BOS) to Seattle (SEA) is requested; the AJAX HTTP response is intercepted, the fare of the higher priced flight in the JSON array is lowered from $600 to $100, and the flight is bought at the lower price.
 
 
The attack is simulated by typing 'javascript:submitData(1234556,11000);' into the browser's address bar. The HTTP GET Ajax request looks exactly the same whether it comes from the javascript functions as intended or is called directly in the browser.
 
 
 
Normally, the the HTML code calls the 'processData()' function which does authorization and validation, then calls the 'submitData' function. The attack bypasses 'processData()' and calls 'submitData' directly.
 
 
 
The goal of the ModSecurity solution is to notify the user when a silent transaction commences.
 
  
 
=== Implementation ===  
 
=== Implementation ===  
  
The ModSecurity solution is to append Javascript to the lesson's start page HTTP response body which extends the the 'submitData' function by displaying a confirmation message box to the user. If they push 'Cancel', the transaction will not go through.
+
The ModSecurity solution will be to persist the actual prices coming from the AJAX request and compare the price of the flight chosen by the user; if they are not the same, the request is blocked.
  
Here is the Javascript code:
+
A snippet of the source code will look like:
 
<pre>
 
<pre>
<script language="JavaScript1.2" type="text/javascript">
+
"flights": [
var oldsubmitData = submitData;
+
{"stops": "0", "transit" : "N/A", "price": "$600"},
 +
{"stops": "2", "transit" : "Newark,Chicago", "price": "$300"}
 +
]
 +
</pre>
  
submitData = function(accountNo, balance)
+
Start the lesson with an empty '*.data' file, but once populate it will have the format of:
{
+
Entry{
   if (confirm("You sure you want to make this transaction?"))
+
   radioindex = 0,
  {
+
   price = 600
    if (oldsubmitData != null)
 
    {
 
      oldsubmitData(accountNo, balance);
 
    }
 
   }
 
  else
 
  {
 
    alert("Transaction canceled at your request");
 
  }
 
 
}
 
}
</script>
 
</pre>
 
  
ModSecurity appends Javascript at the very end of the response body after the '</html>' tag. Even though the resulting format does not adhere to the HTML 4.01 and HTML 5 specifications, current Web browsers will accept this format.
+
Entry{
 
+
  radioindex = 1,
Before incorporating the solution into ModSecurity, test it by intercepting the HTTP response in a web proxy, copy and paste the code after the </html>, then forward the response to the browser.
+
   price = 300
 
+
}
To build the ModSecurity rule, it is necessary to know the format of the GET request. The AJAX request is of the form:
 
<pre>
 
GET http://192.168.0.5/WebGoat/attack?Screen=40&menu=400&from=ajax&newAccount=1234556&amount=11000
 
</pre>
 
 
 
Because of the way WebGoat it is implemented, for this ModSecurity solution to work the lesson must explicitly be restarted by clicking on the link "Restart this Lesson".
 
 
 
The ModSecurity phase 4 rule that appends the Javascript to the response body can only be invoked when it comes from the GET request as shown above. To implement this behavior, it is necessary to set a flag - a session variable - in the request. If a GET request is not the lesson restart, the flag is turned off as a safeguard.
 
 
 
The phase 2 request portion of the configuration file 'rulefile_03-7_silent-transaction.conf' is:
 
<pre>
 
  SecRule &ARGS_GET:Restart "@eq 0" "nolog,skip:4"
 
   SecRule &ARGS_GET:amount "!@eq 0" "nolog,skip:3"
 
  SecRule &ARGS_GET:newAccount "!@eq 0" "nolog,skip:2"
 
  
  SecRule REQUEST_COOKIES:JSESSIONID "!^$" \
 
"chain,log,auditlog,pass,msg:'rulefile_03-7_silent-transaction.conf: \
 
Setting session collection'"
 
  SecAction setsid:%{REQUEST_COOKIES.JSESSIONID}
 
  SecAction "log,setvar:session.lesson037=1,msg:'setting session.lesson037=1 \
 
initially after setsid from rulefile_03-7_silent-transaction.conf',skipAfter:370"
 
  
  # if not the GET request that we want, as a safeguard turn the flag off
+
First, we start with the response body because we persist the flights here.
  SecAction "log,setvar:session.lesson037=0,msg:'setting session.lesson037=0 \
 
initially after setsid from rulefile_03-7_silent-transaction.conf'"
 
  
  SecAction "t:none,allow:request,id:'370'"
+
The phase 4 response portion of the configuration file 'rulefile_03-6_json-injection.conf' is:
</pre>
+
  SecRuleScript "/etc/modsecurity/data/flights-response_03-6.lua" "phase:4,t:none,log,auditlog,allow,msg:'Luascript: AJAX Security -> 3.6 JSON Injection: in RESPONSE; writing flight prices to file'"
  
The first 3 rules filters out the AJAX request and allows only the lesson restart. The next 2 rules creates the session collection, and the following rule sets the flag for that session.
+
Refer to the Lua script 'flights-response_03-6.lua'. The steps are:
 +
- read the response body into a buffer
 +
- extract the information from each flight from the buffer (array index and price) and write to the data file
  
The phase 4 response portion of the configuration file 'rulefile_03-7_silent-transaction.conf' is (the Javascript should be packed on one line):
+
After the price is manipulated and when the purchase is made, the POST parameters are:
<pre>
+
travelFrom=BOS&travelTo=SEA&radio0=on&SUBMIT=Submit&price2Submit=%24100
  # finished if not 'text/html'
 
  SecRule RESPONSE_CONTENT_TYPE "!^text/html" "phase:4,t:none,allow,nolog"
 
  
  SecAction "phase:4,log,initcol:session=%{SESSIONID}, \
+
In this example, zero from 'radio0' has to be extracted to get the correct index in the array; then obtain the price from 'price2Submit'.
msg:'rulefile_03-7_silent-transaction.conf: getting session.lesson037'"
 
  
  # Here check if session variable set;
+
The phase 2 request portion of the configuration file 'rulefile_03-6_json-injection.conf' is:
  #  if so, then override 'submitData' function so that user is prompted
+
   SecRule ARGS:menu "!@eq 400" "phase:2,t:none,skip:4"
   SecRule SESSION:LESSON037 "@eq 1" "phase:4,t:none,log,auditlog,pass, \
+
  SecRule &ARGS_POST:SUBMIT "@eq 0" "nolog,skip:3"
msg:'appending javascript in rulefile_03-7_silent-transaction.conf', \
+
  SecRule &ARGS_POST:price2Submit "@eq 0" "nolog,skip:2"
append:'<script language=\"JavaScript1.2\" type=\"text/javascript\"> \
 
var oldsubmitData = submitData; submitData = function(accountNo, balance)\
 
{if (confirm(\"You sure you want to make this transaction?\")) \
 
{if (oldsubmitData != null) {oldsubmitData(accountNo, balance);}} \
 
else{alert(\"Transaction canceled at your request\");}}</script>'"
 
</pre>
 
  
Since the session collection is accessed in a different phase than it was created, the 'initcol' action must be called to load it.
+
  # action is triggered if script returns non-nil value
 +
  SecRuleScript "/etc/modsecurity/data/flights-request_03-6.lua" "phase:2,t:none,log,auditlog,deny,severity:3,msg:'Luascript: AJAX Security -> 3.6 JSON Injection: An illegal attempt was made to alter the flight price',tag:'INJECTION_ATTACK',redirect:/_error_pages_/lesson03-6.html"
 +
  SecAction "phase:2,allow:request,t:none,log,auditlog,msg:'Luascript: AJAX Security -> 3.6 JSON Injection: no illegal attempts made to alter the flight price'"
  
Also, note that the double quotes in the packed Javascript function must be escaped with a back slash.
+
Refer to the Lua script 'flights-request_03-6.lua'. The steps are:
 
+
- retrieve the POST parameters
If a silent transaction is attempted, a confirmation message box appears:
+
- loop through each argument; extract the radio parameter index (e.g. zero from 'radio0') that is on plus the price parameter value
 
+
- loop through the data file until arriving at the correct index
[[Image:OWASP_ModSecurity_Securing_WebGoat_JSON_Injection_SS0.jpg]]
+
- compare the prices and return an error message if they are not equal
 
 
(screenshot lesson03-7_silent-transaction_solution1.jpg)
 
 
 
 
 
And after cancelling the transaction:
 
 
 
[[Image:OWASP_ModSecurity_Securing_WebGoat_JSON_Injection_SS1.jpg]]
 
 
 
(screenshot lesson03-7_silent-transaction_solution2.jpg)
 
  
 
=== Comments ===
 
=== Comments ===
  
* This lesson demonstrates the use of ModSecurity's Javascript injection function, 'append', and the use of session collections.
+
* This lessons shows how to use a 'do' loop in Lua and retrieve POST parameter names and values
 
 
* 'initcol' has to be used to load and access session collection variables under two scenarios: (1) When the collection is created in an earlier phase; and (2) When the collection is created in a separate ModSecurity configuration file, even if the phase is the same.
 
 
 
* In most cases, using ModSecurity's 'prepend' and 'append' Javascript functionality is useful to enhance the user experience because if the attacker is the user, he/she will simply bypass the Javascript using a web proxy or other means. However, this lesson highlights another use of Javascript injection: when the attacker is not the user and acts as a 3rd party by placing malicious code into the page.
 
 
 
* Unfortunately, the way WebGoat is set up, there is no way to distinguish this Sublesson 3.7 from the other Lesson 3's - all are 'menu=400', so this solution cannot be used with any of the other solutions in Lesson 3. There's one caveat: if somehow it can be the last *.conf file in the group and control properly filters down to it, then it can be used with the other Lesson 3 sublessons.ustrates another use: when the attacker is not the user and acts as a 3rd party and malicious code is placed in the page.
 

Revision as of 07:09, 20 October 2008

3. AJAX Security -> 3.6 JSON Injection

Lesson overview

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

Lesson solution

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

Strategy

This WebGoat lesson, a schedule and airfare for a roundtrip flight from Boston (BOS) to Seattle (SEA) is requested; the AJAX HTTP response is intercepted, the fare of the higher priced flight in the JSON array is lowered from $600 to $100, and the flight is bought at the lower price.

Implementation

The ModSecurity solution will be to persist the actual prices coming from the AJAX request and compare the price of the flight chosen by the user; if they are not the same, the request is blocked.

A snippet of the source code will look like:

"flights": [
{"stops": "0", "transit" : "N/A", "price": "$600"},
{"stops": "2", "transit" : "Newark,Chicago", "price": "$300"} 
]

Start the lesson with an empty '*.data' file, but once populate it will have the format of: Entry{

 radioindex = 0,
 price = 600

}

Entry{

 radioindex = 1,
 price = 300

}


First, we start with the response body because we persist the flights here.

The phase 4 response portion of the configuration file 'rulefile_03-6_json-injection.conf' is:

  SecRuleScript "/etc/modsecurity/data/flights-response_03-6.lua" "phase:4,t:none,log,auditlog,allow,msg:'Luascript: AJAX Security -> 3.6 JSON Injection: in RESPONSE; writing flight prices to file'"

Refer to the Lua script 'flights-response_03-6.lua'. The steps are: - read the response body into a buffer - extract the information from each flight from the buffer (array index and price) and write to the data file

After the price is manipulated and when the purchase is made, the POST parameters are: travelFrom=BOS&travelTo=SEA&radio0=on&SUBMIT=Submit&price2Submit=%24100

In this example, zero from 'radio0' has to be extracted to get the correct index in the array; then obtain the price from 'price2Submit'.

The phase 2 request portion of the configuration file 'rulefile_03-6_json-injection.conf' is:

 SecRule ARGS:menu "!@eq 400" "phase:2,t:none,skip:4"
 SecRule &ARGS_POST:SUBMIT "@eq 0" "nolog,skip:3"
 SecRule &ARGS_POST:price2Submit "@eq 0" "nolog,skip:2"
 # action is triggered if script returns non-nil value
 SecRuleScript "/etc/modsecurity/data/flights-request_03-6.lua" "phase:2,t:none,log,auditlog,deny,severity:3,msg:'Luascript: AJAX Security -> 3.6 JSON Injection: An illegal attempt was made to alter the flight price',tag:'INJECTION_ATTACK',redirect:/_error_pages_/lesson03-6.html"
 SecAction "phase:2,allow:request,t:none,log,auditlog,msg:'Luascript: AJAX Security -> 3.6 JSON Injection: no illegal attempts made to alter the flight price'"

Refer to the Lua script 'flights-request_03-6.lua'. The steps are: - retrieve the POST parameters - loop through each argument; extract the radio parameter index (e.g. zero from 'radio0') that is on plus the price parameter value - loop through the data file until arriving at the correct index - compare the prices and return an error message if they are not equal

Comments

  • This lessons shows how to use a 'do' loop in Lua and retrieve POST parameter names and values