Merge branch 'release/v8.0.0.M18'
This commit is contained in:
commit
12123ef13b
4
platformQuickStarts/GCP/GKE-Docker/deploy.cfg
Normal file
4
platformQuickStarts/GCP/GKE-Docker/deploy.cfg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
CURTAG=webgoat/webgoat-8.0
|
||||||
|
DEST_TAG=gcr.io/astech-training/raging-wire-webgoat
|
||||||
|
CLUSTER_NAME=raging-wire-webgoat
|
||||||
|
PORT_NUM=8080
|
4
platformQuickStarts/GCP/GKE-Docker/gke-deploy-config.sh
Normal file
4
platformQuickStarts/GCP/GKE-Docker/gke-deploy-config.sh
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
CURTAG=webgoat/webgoat-8.0
|
||||||
|
DEST_TAG=gcr.io/your-gke-project/your-webgoat-tag
|
||||||
|
CLUSTER_NAME=your-cluster-name
|
||||||
|
PORT_NUM=8080
|
@ -232,7 +232,7 @@ public class CreateDB {
|
|||||||
|
|
||||||
// Create the new table
|
// Create the new table
|
||||||
try {
|
try {
|
||||||
String createTableStatement = "CREATE TABLE user_system_data (" + "userid varchar(5) not null primary key,"
|
String createTableStatement = "CREATE TABLE user_system_data (" + "userid int not null primary key,"
|
||||||
+ "user_name varchar(12)," + "password varchar(10)," + "cookie varchar(30)" + ")";
|
+ "user_name varchar(12)," + "password varchar(10)," + "cookie varchar(30)" + ")";
|
||||||
statement.executeUpdate(createTableStatement);
|
statement.executeUpdate(createTableStatement);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@ -240,11 +240,11 @@ public class CreateDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Populate
|
// Populate
|
||||||
String insertData1 = "INSERT INTO user_system_data VALUES ('101','jsnow','passwd1', '')";
|
String insertData1 = "INSERT INTO user_system_data VALUES (101,'jsnow','passwd1', '')";
|
||||||
String insertData2 = "INSERT INTO user_system_data VALUES ('102','jdoe','passwd2', '')";
|
String insertData2 = "INSERT INTO user_system_data VALUES (102,'jdoe','passwd2', '')";
|
||||||
String insertData3 = "INSERT INTO user_system_data VALUES ('103','jplane','passwd3', '')";
|
String insertData3 = "INSERT INTO user_system_data VALUES (103,'jplane','passwd3', '')";
|
||||||
String insertData4 = "INSERT INTO user_system_data VALUES ('104','jeff','jeff', '')";
|
String insertData4 = "INSERT INTO user_system_data VALUES (104,'jeff','jeff', '')";
|
||||||
String insertData5 = "INSERT INTO user_system_data VALUES ('105','dave','dave', '')";
|
String insertData5 = "INSERT INTO user_system_data VALUES (105,'dave','passW0rD', '')";
|
||||||
statement.executeUpdate(insertData1);
|
statement.executeUpdate(insertData1);
|
||||||
statement.executeUpdate(insertData2);
|
statement.executeUpdate(insertData2);
|
||||||
statement.executeUpdate(insertData3);
|
statement.executeUpdate(insertData3);
|
||||||
|
@ -17,7 +17,7 @@ public class UserForm {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Size(min=6, max=20)
|
@Size(min=6, max=20)
|
||||||
@Pattern(regexp = "[a-zA-Z0-9]*", message = "can only contain letters and digits")
|
@Pattern(regexp = "[a-zA-Z0-9-]*", message = "can only contain letters, digits, and -")
|
||||||
private String username;
|
private String username;
|
||||||
@NotNull
|
@NotNull
|
||||||
@Size(min=6, max=10)
|
@Size(min=6, max=10)
|
||||||
|
@ -79,6 +79,7 @@ define(['jquery',
|
|||||||
this.listenTo(this.lessonHintView, 'hints:hideButton', this.onHideHintsButton);
|
this.listenTo(this.lessonHintView, 'hints:hideButton', this.onHideHintsButton);
|
||||||
this.lessonContentView.navToPage(pageNum);
|
this.lessonContentView.navToPage(pageNum);
|
||||||
this.lessonHintView.hideHints();
|
this.lessonHintView.hideHints();
|
||||||
|
this.lessonHintView.showFirstHint();
|
||||||
//this.lessonHintView.selectHints();
|
//this.lessonHintView.selectHints();
|
||||||
this.titleView.render(this.lessonInfoModel.get('lessonTitle'));
|
this.titleView.render(this.lessonInfoModel.get('lessonTitle'));
|
||||||
return;
|
return;
|
||||||
@ -160,7 +161,7 @@ define(['jquery',
|
|||||||
}
|
}
|
||||||
//
|
//
|
||||||
this.lessonHintView.render();
|
this.lessonHintView.render();
|
||||||
if (this.lessonHintView.getHintsCount > 0) {
|
if (this.lessonHintView.getHintsCount() > 0) {
|
||||||
this.helpControlsView.showHintsButton();
|
this.helpControlsView.showHintsButton();
|
||||||
} else {
|
} else {
|
||||||
this.helpControlsView.hideHintsButton();
|
this.helpControlsView.hideHintsButton();
|
||||||
|
@ -32,7 +32,11 @@ define(['jquery',
|
|||||||
}
|
}
|
||||||
this.set('content',content);
|
this.set('content',content);
|
||||||
this.set('lessonUrl',document.URL.replace(/\.lesson.*/,'.lesson'));
|
this.set('lessonUrl',document.URL.replace(/\.lesson.*/,'.lesson'));
|
||||||
|
if (/.*\.lesson\/(\d{1,4})$/.test(document.URL)) {
|
||||||
this.set('pageNum',document.URL.replace(/.*\.lesson\/(\d{1,4})$/,'$1'));
|
this.set('pageNum',document.URL.replace(/.*\.lesson\/(\d{1,4})$/,'$1'));
|
||||||
|
} else {
|
||||||
|
this.set('pageNum',0);
|
||||||
|
}
|
||||||
this.trigger('content:loaded',this,loadHelps);
|
this.trigger('content:loaded',this,loadHelps);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -32,21 +32,19 @@ function($,
|
|||||||
|
|
||||||
toggleLabel: function() {
|
toggleLabel: function() {
|
||||||
if (this.isVisible()) {
|
if (this.isVisible()) {
|
||||||
$('show-hints-button').text('Hide hints');
|
$('#show-hints-button').text('Hide hints');
|
||||||
} else {
|
} else {
|
||||||
$('show-hints-button').text('Show hints');
|
$('#show-hints-button').text('Show hints');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
render:function() {
|
render:function() {
|
||||||
if (this.isVisible()) {
|
if (this.isVisible()) {
|
||||||
this.$el.hide(350);
|
this.$el.hide(350, this.toggleLabel.bind(this));
|
||||||
} else if (this.hintsToShow.length > 0) {
|
} else if (this.hintsToShow.length > 0) {
|
||||||
this.$el.show(350);
|
this.$el.show(350, this.toggleLabel.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.toggleLabel()
|
|
||||||
|
|
||||||
if (this.hintsToShow.length > 0) {
|
if (this.hintsToShow.length > 0) {
|
||||||
this.hideShowPrevNextButtons();
|
this.hideShowPrevNextButtons();
|
||||||
}
|
}
|
||||||
@ -90,7 +88,7 @@ function($,
|
|||||||
|
|
||||||
hideHints: function() {
|
hideHints: function() {
|
||||||
if (this.$el.is(':visible')) {
|
if (this.$el.is(':visible')) {
|
||||||
this.$el.hide(350);
|
this.$el.hide(350, this.toggleLabel.bind(this));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -106,6 +104,12 @@ function($,
|
|||||||
this.displayHint(this.curHint);
|
this.displayHint(this.curHint);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showFirstHint: function() {
|
||||||
|
this.curHint = 0;
|
||||||
|
this.hideShowPrevNextButtons();
|
||||||
|
this.displayHint(this.curHint);
|
||||||
|
},
|
||||||
|
|
||||||
displayHint: function(curHint) {
|
displayHint: function(curHint) {
|
||||||
if(this.hintsToShow.length == 0) {
|
if(this.hintsToShow.length == 0) {
|
||||||
// this.hideHints();
|
// this.hideHints();
|
||||||
|
@ -123,8 +123,9 @@
|
|||||||
<section class="main-content-wrapper">
|
<section class="main-content-wrapper">
|
||||||
<section id="main-content"> <!--ng-controller="goatLesson"-->
|
<section id="main-content"> <!--ng-controller="goatLesson"-->
|
||||||
<div id="lesson-page" class="pages">
|
<div id="lesson-page" class="pages">
|
||||||
<span th:text="${numUsers}"> Users in WebGoat</span>
|
<span th:text="${numUsers}"></span>
|
||||||
<!-- iterate over users below -->su
|
<span> Users in WebGoat</span>
|
||||||
|
|
||||||
<div sec:authorize="hasAuthority('WEBGOAT_ADMIN')">
|
<div sec:authorize="hasAuthority('WEBGOAT_ADMIN')">
|
||||||
<h3>WebGoat Users</h3>
|
<h3>WebGoat Users</h3>
|
||||||
<div th:each="user : ${allUsers}">
|
<div th:each="user : ${allUsers}">
|
||||||
|
@ -49,10 +49,7 @@ import org.owasp.encoder.*;
|
|||||||
|
|
||||||
import static org.springframework.http.MediaType.ALL_VALUE;
|
import static org.springframework.http.MediaType.ALL_VALUE;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
||||||
|
|
||||||
@ -78,14 +75,13 @@ public class StoredXssComments extends AssignmentEndpoint {
|
|||||||
@RequestMapping(method = GET, produces = MediaType.APPLICATION_JSON_VALUE,consumes = ALL_VALUE)
|
@RequestMapping(method = GET, produces = MediaType.APPLICATION_JSON_VALUE,consumes = ALL_VALUE)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Collection<Comment> retrieveComments() {
|
public Collection<Comment> retrieveComments() {
|
||||||
Collection<Comment> allComments = Lists.newArrayList();
|
List<Comment> allComments = Lists.newArrayList();
|
||||||
Collection<Comment> newComments = userComments.get(webSession.getUserName());
|
Collection<Comment> newComments = userComments.get(webSession.getUserName());
|
||||||
|
allComments.addAll(comments);
|
||||||
if (newComments != null) {
|
if (newComments != null) {
|
||||||
allComments.addAll(newComments);
|
allComments.addAll(newComments);
|
||||||
}
|
}
|
||||||
|
Collections.reverse(allComments);
|
||||||
allComments.addAll(comments);
|
|
||||||
|
|
||||||
return allComments;
|
return allComments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ xss-reflected-5a-failure=Try again. We do want to see this specific javascript (
|
|||||||
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>
|
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>
|
||||||
xss-reflected-5b-failure=Nope, pretty easy to guess now though.
|
xss-reflected-5b-failure=Nope, pretty easy to guess now though.
|
||||||
xss-reflected-6a-success=Correct! Now, see if you can send in an exploit to that route in the next assignment.
|
xss-reflected-6a-success=Correct! Now, see if you can send in an exploit to that route in the next assignment.
|
||||||
xss-reflected-6a-failure=No, look at the example. Check the GoatRouter.js file. It should be pretty easy to determine.
|
xss-reflected-6a-failure=No, look at the example. Check the <a href="/WebGoat/js/goatApp/view/GoatRouter.js" target="_blank">GoatRouter.js</a> file. It should be pretty easy to determine.
|
||||||
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-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-dom-message-failure=Incorrect, keep trying. It should be obvious in the log when you are successful.
|
||||||
|
@ -4,7 +4,7 @@ You should have been able to execute script with the last example. At this point
|
|||||||
|
|
||||||
Why is that?
|
Why is that?
|
||||||
|
|
||||||
That is because there is no link that would tigger that XSS.
|
That is because there is no link that would trigger that XSS.
|
||||||
You can try it yourself to see what happens ... go to (substitute localhost with your server's name or IP if you need to):
|
You can try it yourself to see what happens ... go to (substitute localhost with your server's name or IP if you need to):
|
||||||
|
|
||||||
link: http://localhost:8080/WebGoat/CrossSiteScripting/attack5a?QTY1=1&QTY2=1&QTY3=1&QTY4=1&field1=<script>alert('myjavascripthere')</script>4128+3214+0002+1999&field2=111
|
link: http://localhost:8080/WebGoat/CrossSiteScripting/attack5a?QTY1=1&QTY2=1&QTY3=1&QTY4=1&field1=<script>alert('my%20javascript%20here')</script>4128+3214+0002+1999&field2=111
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
== Ientify Potential for DOM-Based XSS
|
== Identify Potential for DOM-Based XSS
|
||||||
|
|
||||||
DOM-Based XSS can usually be found by looking for the route configurations in the client-side code.
|
DOM-Based XSS can usually be found by looking for the route configurations in the client-side code.
|
||||||
Look for a route that takes inputs that you can ID being 'reflected' to the page.
|
Look for a route that takes inputs that are being 'reflected' to the page.
|
||||||
|
|
||||||
For this example, you'll want to look for some 'test' code in the route handlers (WebGoat uses backbone as its primary javascript library).
|
For this example, you'll want to look for some 'test' code in the route handlers (WebGoat uses backbone as its primary javascript library).
|
||||||
Sometimes, test code gets left in production (and often times test code is very simple and lacks security or any quality controls!).
|
Sometimes, test code gets left in production (and 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 ...
|
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:
|
it should look something like /WebGoat/start.mvc#lesson/CrossSiteScripting.lesson/9. The 'base route' in this case is:
|
||||||
*start.mvc#lesson/*
|
*start.mvc#lesson/*
|
||||||
|
The *CrossSiteScripting.lesson/9* after that are parameters that are processed by the javascript route handler.
|
||||||
|
|
||||||
The *CrossSiteScripting.lesson/#* after that are parameters that are processed by javascript route handler.
|
So, what is the route for the test code that stayed in the app during production?
|
||||||
|
To answer this question, you have to check the javascript source.
|
||||||
So, what is test route for this test code?
|
|
@ -8,4 +8,4 @@ The function you want to execute is ...
|
|||||||
|
|
||||||
Sure, you could just use console/debug to trigger it, but you need to trigger it via a URL in a new tab.
|
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 do trigger it, a subsequent response will come to the browser with a random number. Put that random number in below.
|
Once you do trigger it, a subsequent response will come to your browser's console with a random number. Put that random number in below.
|
||||||
|
@ -64,11 +64,11 @@ public class CSRFFeedback extends AssignmentEndpoint {
|
|||||||
|
|
||||||
private boolean hostOrRefererDifferentHost(HttpServletRequest request) {
|
private boolean hostOrRefererDifferentHost(HttpServletRequest request) {
|
||||||
String referer = request.getHeader("referer");
|
String referer = request.getHeader("referer");
|
||||||
String origin = request.getHeader("origin");
|
String host = request.getHeader("host");
|
||||||
if (referer != null) {
|
if (referer != null) {
|
||||||
return !referer.contains(origin);
|
return !referer.contains(host);
|
||||||
} else {
|
} else {
|
||||||
return true; //this case referer is null or origin does not matter we cannot compare so we return true which should of course be false
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
action="/WebGoat/csrf/basic-get-flag"
|
action="/WebGoat/csrf/basic-get-flag"
|
||||||
enctype="application/json;charset=UTF-8">
|
enctype="application/json;charset=UTF-8">
|
||||||
<input name="csrf" type="hidden" value="false"/>
|
<input name="csrf" type="hidden" value="false"/>
|
||||||
<input type="submit" name="ubmit="/>
|
<input type="submit" name="submit"/>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
@ -16,9 +16,11 @@ the activities of the user.
|
|||||||
image::images/login-csrf.png[caption="Figure: ", title="Login CSRF from Robust Defenses for Cross-Site Request Forgery", width="800", height="500", style="lesson-image" link="http://seclab.stanford.edu/websec/csrf/csrf.pdf"]
|
image::images/login-csrf.png[caption="Figure: ", title="Login CSRF from Robust Defenses for Cross-Site Request Forgery", width="800", height="500", style="lesson-image" link="http://seclab.stanford.edu/websec/csrf/csrf.pdf"]
|
||||||
|
|
||||||
{blank}
|
{blank}
|
||||||
For more information read the following http://seclab.stanford.edu/websec/csrf/csrf.pdf[paper]
|
For more information read the following http://seclab.stanford.edu/websec/csrf/csrf.pdf[paper].
|
||||||
|
|
||||||
In this assignment try to see if WebGoat is also vulnerable for a login CSRF attack. First create a user
|
In this assignment try to see if WebGoat is also vulnerable for a login CSRF attack.
|
||||||
based on your own username prefixed with csrf. So if your username is `tom` you must create
|
Leave this tab open and in another tab create a user based on your own username prefixed with `csrf-`.
|
||||||
a new user called `csrf-tom`
|
So if your username is `tom` you must create a new user called `csrf-tom`.
|
||||||
|
|
||||||
|
Login as the new user. This is what an attacker would do using CSRF. Then click the button in the original tab.
|
||||||
|
Because you are logged in as a different user, the attacker learns that you clicked the button.
|
||||||
|
@ -46,7 +46,7 @@ public class CSRFFeedbackTest extends LessonTest {
|
|||||||
mockMvc.perform(post("/csrf/feedback/message")
|
mockMvc.perform(post("/csrf/feedback/message")
|
||||||
.contentType(MediaType.TEXT_PLAIN)
|
.contentType(MediaType.TEXT_PLAIN)
|
||||||
.cookie(new Cookie("JSESSIONID", "test"))
|
.cookie(new Cookie("JSESSIONID", "test"))
|
||||||
.header("origin", "localhost:8080")
|
.header("host", "localhost:8080")
|
||||||
.header("referer", "webgoat.org")
|
.header("referer", "webgoat.org")
|
||||||
.content("{\"name\": \"Test\", \"email\": \"test1233@dfssdf.de\", \"subject\": \"service\", \"message\":\"dsaffd\"}"))
|
.content("{\"name\": \"Test\", \"email\": \"test1233@dfssdf.de\", \"subject\": \"service\", \"message\":\"dsaffd\"}"))
|
||||||
.andExpect(jsonPath("lessonCompleted", is(true)))
|
.andExpect(jsonPath("lessonCompleted", is(true)))
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package org.owasp.webgoat.plugin;
|
package org.owasp.webgoat.plugin;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import io.jsonwebtoken.impl.TextCodec;
|
||||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||||
@ -23,7 +24,7 @@ import java.util.List;
|
|||||||
@AssignmentHints({"jwt-secret-hint1", "jwt-secret-hint2", "jwt-secret-hint3"})
|
@AssignmentHints({"jwt-secret-hint1", "jwt-secret-hint2", "jwt-secret-hint3"})
|
||||||
public class JWTSecretKeyEndpoint extends AssignmentEndpoint {
|
public class JWTSecretKeyEndpoint extends AssignmentEndpoint {
|
||||||
|
|
||||||
public static final String JWT_SECRET = "victory";
|
public static final String JWT_SECRET = TextCodec.BASE64.encode("victory");
|
||||||
private static final String WEBGOAT_USER = "WebGoat";
|
private static final String WEBGOAT_USER = "WebGoat";
|
||||||
private static final List<String> expectedClaims = Lists.newArrayList("iss", "iat", "exp", "aud", "sub", "username", "Email", "Role");
|
private static final List<String> expectedClaims = Lists.newArrayList("iss", "iat", "exp", "aud", "sub", "username", "Email", "Role");
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import io.jsonwebtoken.Claims;
|
|||||||
import io.jsonwebtoken.Jwt;
|
import io.jsonwebtoken.Jwt;
|
||||||
import io.jsonwebtoken.JwtException;
|
import io.jsonwebtoken.JwtException;
|
||||||
import io.jsonwebtoken.Jwts;
|
import io.jsonwebtoken.Jwts;
|
||||||
|
import io.jsonwebtoken.impl.TextCodec;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||||
@ -25,7 +26,6 @@ import java.time.Duration;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import static java.util.Comparator.comparingLong;
|
import static java.util.Comparator.comparingLong;
|
||||||
import static java.util.Optional.ofNullable;
|
import static java.util.Optional.ofNullable;
|
||||||
@ -39,7 +39,7 @@ import static java.util.stream.Collectors.toList;
|
|||||||
@AssignmentHints({"jwt-change-token-hint1", "jwt-change-token-hint2", "jwt-change-token-hint3", "jwt-change-token-hint4", "jwt-change-token-hint5"})
|
@AssignmentHints({"jwt-change-token-hint1", "jwt-change-token-hint2", "jwt-change-token-hint3", "jwt-change-token-hint4", "jwt-change-token-hint5"})
|
||||||
public class JWTVotesEndpoint extends AssignmentEndpoint {
|
public class JWTVotesEndpoint extends AssignmentEndpoint {
|
||||||
|
|
||||||
public static final String JWT_PASSWORD = "victory";
|
public static final String JWT_PASSWORD = TextCodec.BASE64.encode("victory");
|
||||||
private static String validUsers = "TomJerrySylvester";
|
private static String validUsers = "TomJerrySylvester";
|
||||||
|
|
||||||
private static int totalVotes = 38929;
|
private static int totalVotes = 38929;
|
||||||
|
@ -63,15 +63,15 @@ whether the location is still the same if not revoke all the tokens and let the
|
|||||||
=== Need for refresh tokens
|
=== Need for refresh tokens
|
||||||
|
|
||||||
Does it make sense to use a refresh token in a modern single page application (SPA)? As we have seen in the section
|
Does it make sense to use a refresh token in a modern single page application (SPA)? As we have seen in the section
|
||||||
about storing tokens there are two option: web storage or a cookie which mean a refresh token is right beside an
|
about storing tokens there are two options: web storage or a cookie which mean a refresh token is right beside an
|
||||||
access token, so if the access token is leaked changes are the refresh token will also be compromised. Most of the time
|
access token, so if the access token is leaked chances are the refresh token will also be compromised. Most of the time
|
||||||
there is a difference of course, the access token is send when you make an API call, the refresh token is only send
|
there is a difference of course. The access token is sent when you make an API call, the refresh token is only sent
|
||||||
when a new access token should be obtained, which in most cases is a different endpoint. If you end up on the same
|
when a new access token should be obtained, which in most cases is a different endpoint. If you end up on the same
|
||||||
server you can chose to only use the access token.
|
server you can choose to only use the access token.
|
||||||
|
|
||||||
As stated above using an access token and a separate refresh token gives some leverage for the server not to check
|
As stated above using an access token and a separate refresh token gives some leverage for the server not to check
|
||||||
the access token over and over. Only perform the check when the user needs a new access token.
|
the access token over and over. Only perform the check when the user needs a new access token.
|
||||||
It is certainly possible to only use an access token, at the server you store the exact same information you would
|
It is certainly possible to only use an access token. At the server you store the exact same information you would
|
||||||
store for a refresh token, see previous paragraph. This way you need to check the token each time but this might
|
store for a refresh token, see previous paragraph. This way you need to check the token each time but this might
|
||||||
be suitable depending on the application. In the case the refresh tokens are stored for validation it is important to protect these tokens as well (at least
|
be suitable depending on the application. In the case the refresh tokens are stored for validation it is important to protect these tokens as well (at least
|
||||||
use a hash function to store them in your database).
|
use a hash function to store them in your database).
|
||||||
|
@ -9,5 +9,5 @@ dictionary attack is not feasible. Once you have a token you can start an offlin
|
|||||||
Given we have the following token try to find out secret key and submit a new key with the userId changed to WebGoat.
|
Given we have the following token try to find out secret key and submit a new key with the userId changed to WebGoat.
|
||||||
|
|
||||||
```
|
```
|
||||||
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJpYXQiOjE1MjQyMTA5MDQsImV4cCI6MTYxODkwNTMwNCwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJ0b21Ad2ViZ29hdC5jb20iLCJ1c2VybmFtZSI6IlRvbSIsIkVtYWlsIjoidG9tQHdlYmdvYXQuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.m-jSyfYEsVzD3CBI6N39wZ7AcdKdp_GiO7F_Ym12u-0
|
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJpYXQiOjE1MjQyMTA5MDQsImV4cCI6MTYxODkwNTMwNCwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJ0b21Ad2ViZ29hdC5jb20iLCJ1c2VybmFtZSI6IlRvbSIsIkVtYWlsIjoidG9tQHdlYmdvYXQuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.vPe-qQPOt78zK8wrbN1TjNJj3LeX9Qbch6oo23RUJgM
|
||||||
```
|
```
|
@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
|||||||
@AssignmentPath("/access-control/user-hash")
|
@AssignmentPath("/access-control/user-hash")
|
||||||
@AssignmentHints({"access-control.hash.hint1","access-control.hash.hint2","access-control.hash.hint3",
|
@AssignmentHints({"access-control.hash.hint1","access-control.hash.hint2","access-control.hash.hint3",
|
||||||
"access-control.hash.hint4","access-control.hash.hint5","access-control.hash.hint6","access-control.hash.hint7",
|
"access-control.hash.hint4","access-control.hash.hint5","access-control.hash.hint6","access-control.hash.hint7",
|
||||||
"access-control.hash.hint8","access-control.hash.hint9"})
|
"access-control.hash.hint8","access-control.hash.hint9","access-control.hash.hint10","access-control.hash.hint11","access-control.hash.hint12"})
|
||||||
public class MissingFunctionACYourHash extends AssignmentEndpoint {
|
public class MissingFunctionACYourHash extends AssignmentEndpoint {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -51,7 +51,7 @@ public class Users extends Endpoint{
|
|||||||
userMap.put("cc", results.getString(3));
|
userMap.put("cc", results.getString(3));
|
||||||
userMap.put("ccType", results.getString(4));
|
userMap.put("ccType", results.getString(4));
|
||||||
userMap.put("cookie", results.getString(5));
|
userMap.put("cookie", results.getString(5));
|
||||||
userMap.put("loginCOunt",Integer.toString(results.getInt(6)));
|
userMap.put("loginCount",Integer.toString(results.getInt(6)));
|
||||||
allUsersMap.put(id,userMap);
|
allUsersMap.put(id,userMap);
|
||||||
}
|
}
|
||||||
userSessionData.setValue("allUsers",allUsersMap);
|
userSessionData.setValue("allUsers",allUsersMap);
|
||||||
|
@ -11,12 +11,15 @@ access-control.hidden-menus.hint3=Look for something a super-user or administato
|
|||||||
access-control.hash.success=Congrats! You really succeeded when you added the user.
|
access-control.hash.success=Congrats! You really succeeded when you added the user.
|
||||||
access-control.hash.close=Keep trying, this one may take several attempts & steps to achieve. See the hints for help.
|
access-control.hash.close=Keep trying, this one may take several attempts & steps to achieve. See the hints for help.
|
||||||
|
|
||||||
access-control.hash.hint1=If you haven't found the hidden menus from the earlier exercise, go do that now.
|
access-control.hash.hint1=There is an easier way and a 'harder' way to achieve this, the easier way involves one simple change in a GET request.
|
||||||
access-control.hash.hint2=When you look at the users page, there is a hint that more info is viewable by a given role of user.
|
access-control.hash.hint2= If you haven't found the hidden menus from the earlier exercise, go do that first.
|
||||||
access-control.hash.hint3=Have you tried tampering the GET request? Can you find supported or unsupported methods? Can you trigger 500 errors?
|
access-control.hash.hint3=When you look at the users page, there is a hint that more info is viewable by a given role.
|
||||||
access-control.hash.hint4=There are actually two ways to solve this one. The first involves just changing a request header.
|
access-control.hash.hint4=For the easy way, have you tried tampering the GET request? Different content-types?
|
||||||
access-control.hash.hint5=If the request to view users, were a 'service' or 'RESTful' endpoint, what would be different about it?
|
access-control.hash.hint5=For the 'easy' way, modify the GET request to /users to include 'Content-Type: application/json'
|
||||||
access-control.hash.hint6=If you're still looking for hints ... try changing the Content-type header in the GET request.
|
access-control.hash.hint6=Now for the harder way ... it builds on the easier way
|
||||||
access-control.hash.hint7=The harder way involves changing the Content-type AND the method ... As well as a proper payload for the request. Look at how registration works first and extrapolate out from there.
|
access-control.hash.hint7=If the request to view users, were a 'service' or 'RESTful' endpoint, what would be different about it?
|
||||||
access-control.hash.hint8=See if you can add a user with a webgoat admin role, and if more is visible once you log in as that user.
|
access-control.hash.hint8=If you're still looking for hints ... try changing the Content-type header as in the GET request.
|
||||||
access-control.hash.hint9=If you create a new user with the admin role ... The role should include 'WEBGOAT' and 'ADMIN' in the role name. You'll have to do some guessing beyond that.
|
access-control.hash.hint9=You also need to deliver a proper payload for the request (look at how registration works). This should be formatted in line with the content-type you just defined.
|
||||||
|
access-control.hash.hint10=You will want to add WEBGOAT_ADMIN for the user's role. Yes, you'd have to guess/fuzz this in a real-world setting.
|
||||||
|
access-control.hash.hint11=OK, here it is. First, create an admin user ... Change the method to POST, change the content-type to "application/json". And your payload should look something like: {"username":"newUser2","password":"newUser12","matchingPassword":"newUser12","role":"WEBGOAT_ADMIN"}
|
||||||
|
access-control.hash.hint12=Now log in as that user and bring up WebGoat/users. Copy your hash and log back in to your original account and input it there to get credit.
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.plugin.introduction;
|
package org.owasp.webgoat.plugin.advanced;
|
||||||
|
|
||||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||||
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.plugin.introduction.SqlInjectionLesson5a;
|
||||||
import org.owasp.webgoat.session.DatabaseUtilities;
|
import org.owasp.webgoat.session.DatabaseUtilities;
|
||||||
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;
|
||||||
@ -55,7 +56,6 @@ public class SqlInjectionLesson6a extends AssignmentEndpoint {
|
|||||||
AttackResult completed(@RequestParam String userid_6a) throws IOException {
|
AttackResult completed(@RequestParam String userid_6a) throws IOException {
|
||||||
return injectableQuery(userid_6a);
|
return injectableQuery(userid_6a);
|
||||||
// The answer: Smith' union select userid,user_name, password,cookie,cookie, cookie,userid from user_system_data --
|
// The answer: Smith' union select userid,user_name, password,cookie,cookie, cookie,userid from user_system_data --
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AttackResult injectableQuery(String accountName) {
|
protected AttackResult injectableQuery(String accountName) {
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.plugin.introduction;
|
package org.owasp.webgoat.plugin.advanced;
|
||||||
|
|
||||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
import org.owasp.webgoat.assignments.AssignmentPath;
|
@ -3,7 +3,7 @@
|
|||||||
Lets try to exploit a join to another table. One of the tables in the WebGoat database is:
|
Lets try to exploit a join to another table. One of the tables in the WebGoat database is:
|
||||||
|
|
||||||
-------------------------------------------------------
|
-------------------------------------------------------
|
||||||
CREATE TABLE user_system_data (userid varchar(5) not null primary key,
|
CREATE TABLE user_system_data (userid int not null primary key,
|
||||||
user_name varchar(12),
|
user_name varchar(12),
|
||||||
password varchar(10),
|
password varchar(10),
|
||||||
cookie varchar(30));
|
cookie varchar(30));
|
||||||
|
@ -64,7 +64,7 @@ public class SqlInjectionLesson6aTest extends LessonTest {
|
|||||||
|
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.lessonCompleted", is(true)))
|
.andExpect(jsonPath("$.lessonCompleted", is(true)))
|
||||||
.andExpect(jsonPath("$.feedback", containsString("dave")));
|
.andExpect(jsonPath("$.feedback", containsString("passW0rD")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -30,7 +30,7 @@ public class SqlInjectionLesson6bTest extends LessonTest {
|
|||||||
@Test
|
@Test
|
||||||
public void submitCorrectPassword() throws Exception {
|
public void submitCorrectPassword() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6b")
|
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6b")
|
||||||
.param("userid_6b", "dave"))
|
.param("userid_6b", "passW0rD"))
|
||||||
|
|
||||||
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(true)));
|
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(true)));
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user