More JWT work

This commit is contained in:
Nanne Baars 2018-05-21 12:41:37 +02:00
parent 7a0820bf89
commit 9d7886d572
13 changed files with 355 additions and 39 deletions

View File

@ -81,6 +81,39 @@ public class CreateDB {
}
}
/**
* Description of the Method
*
* @param connection Description of the Parameter
* @throws SQLException Description of the Exception
*/
private void createJWTKeys(Connection connection) throws SQLException {
Statement statement = connection.createStatement();
// Drop servers table
try {
String dropTable = "DROP TABLE jwt_keys";
statement.executeUpdate(dropTable);
} catch (SQLException e) {
System.out.println("Info - Could not drop jwtkeys table");
}
// Create the new table
try {
String createTableStatement = "CREATE TABLE jwt_keys"
+ " (" + "id varchar(10),"
+ "key varchar(20))";
statement.executeUpdate(createTableStatement);
String insertData1 = "INSERT INTO jwt_keys VALUES ('webgoat_key', 'qwertyqwerty1234')";
String insertData2 = "INSERT INTO jwt_keys VALUES ('webwolf_key', 'doesnotreallymatter')";
statement.executeUpdate(insertData1);
statement.executeUpdate(insertData2);
} catch (SQLException e) {
System.out.println("Error creating product table " + e.getLocalizedMessage());
}
}
/**
* Description of the Method
@ -975,6 +1008,7 @@ public class CreateDB {
createTanTable(connection);
createMFEImagesTable(connection);
createModifyWithSQLLessonTable(connection);
createJWTKeys(connection);
System.out.println("Success: creating tables.");
}
}

View File

@ -0,0 +1,85 @@
package org.owasp.webgoat.plugin;
import com.google.common.base.Charsets;
import io.jsonwebtoken.*;
import org.apache.commons.lang3.StringUtils;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentHints;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.owasp.webgoat.session.DatabaseUtilities;
import org.owasp.webgoat.session.WebSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author nbaars
* @since 4/23/17.
*/
@AssignmentPath("/JWT/final")
@AssignmentHints({"jwt-final-hint1", "jwt-final-hint2", "jwt-final-hint3", "jwt-final-hint4", "jwt-final-hint5", "jwt-final-hint6"})
public class JWTFinalEndpoint extends AssignmentEndpoint {
@Autowired
private WebSession webSession;
@PostMapping("follow/{user}")
public @ResponseBody
String follow(@PathVariable("user") String user) {
if ("Jerry".equals(user)) {
return "Following yourself seems redundant";
} else {
return "You are now following Tom";
}
}
@PostMapping("delete")
public @ResponseBody
AttackResult resetVotes(@RequestParam("token") String token) {
if (StringUtils.isEmpty(token)) {
return trackProgress(failed().feedback("jwt-invalid-token").build());
} else {
try {
final String[] errorMessage = {null};
Jwt jwt = Jwts.parser().setSigningKeyResolver(new SigningKeyResolverAdapter() {
@Override
public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) {
final String kid = (String) header.get("kid");
try {
Connection connection = DatabaseUtilities.getConnection(webSession);
ResultSet rs = connection.createStatement().executeQuery("SELECT key FROM jwt_keys WHERE id = " + kid);
while (rs.next()) {
return rs.getString(1).getBytes(Charsets.UTF_8);
}
} catch (SQLException e) {
errorMessage[0] = e.getMessage();
}
return null;
}
}).parse(token);
if (errorMessage[0] != null) {
return trackProgress(failed().output(errorMessage[0]).build());
}
Claims claims = (Claims) jwt.getBody();
String username = (String) claims.get("username");
if ("Jerry".equals(username)) {
return trackProgress(failed().feedback("jwt-final-jerry-account").build());
}
if ("Tom".equals(username)) {
return trackProgress(success().build());
} else {
return trackProgress(failed().feedback("jwt-final-not-tom").build());
}
} catch (JwtException e) {
return trackProgress(failed().feedback("jwt-invalid-token").output(e.toString()).build());
}
}
}
}

View File

@ -0,0 +1,9 @@
package org.owasp.webgoat.plugin.refresh;
public class RefreshEndpoint {
private static final String TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE0OTUyODU3NDUsImV4cCI6MTQ5NTI4NTc0NSwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJ0b21Ad2ViZ29hdC5vcmciLCJuYW1lIjoiVG9tIiwiZW1haWwiOiJ0b21Ad2ViZ29hdC5jb20iLCJyb2xlIjoiQ2F0In0.NTd3E17JZlVKZk52Wq_AWZ0RDafV5KDRMuJner_zUn4";
private static final String JWT_KEY = "qwertyuiopasdfghjklzxcvbnm123456";
}

View File

@ -10,3 +10,107 @@ a.list-group-item.active small {
.img-responsive {
min-width: 100%;
}
.card {
font-size: 1em;
overflow: hidden;
padding: 0;
border: none;
border-radius: .28571429rem;
box-shadow: 0 1px 3px 0 #d4d4d5, 0 0 0 1px #d4d4d5;
}
.card-block {
font-size: 1em;
position: relative;
margin: 0;
padding: 1em;
border: none;
border-top: 1px solid rgba(34, 36, 38, .1);
box-shadow: none;
}
.card-img-top {
display: block;
width: 100%;
height: auto;
}
.card-title {
font-size: 1.28571429em;
font-weight: 700;
line-height: 1.2857em;
}
.card-text {
clear: both;
margin-top: .5em;
color: rgba(0, 0, 0, .68);
}
.card-footer {
font-size: 1em;
position: static;
top: 0;
left: 0;
max-width: 100%;
padding: .75em 1em;
color: rgba(0, 0, 0, .4);
border-top: 1px solid rgba(0, 0, 0, .05) !important;
background: #fff;
}
.card-inverse .btn {
border: 1px solid rgba(0, 0, 0, .05);
}
.profile {
position: absolute;
top: -12px;
display: inline-block;
overflow: hidden;
box-sizing: border-box;
width: 50px;
height: 50px;
margin: 0;
border: 1px solid #fff;
border-radius: 50%;
}
.profile-avatar {
display: block;
width: 100%;
height: auto;
border-radius: 50%;
}
.profile-inline {
position: relative;
top: 0;
display: inline-block;
}
.profile-inline ~ .card-title {
display: inline-block;
margin-left: 4px;
vertical-align: top;
}
.text-bold {
font-weight: 700;
}
.meta {
font-size: 1em;
color: rgba(0, 0, 0, .4);
}
.meta a {
text-decoration: none;
color: rgba(0, 0, 0, .4);
}
.meta a:hover {
color: rgba(0, 0, 0, .87);
}

View File

@ -106,11 +106,11 @@
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:JWT_refresh_assignment"></div>
<div class="adoc-content" th:replace="doc:JWT_refresh_assignment.adoc"></div>
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/jwt.css}"/>
<script th:src="@{/lesson_js/bootstrap.min.js}" language="JavaScript"></script>
<script th:src="@{/lesson_js/jwt-signing.js}" language="JavaScript"></script>
<script th:src="@{/lesson_js/jwt-final.js}" language="JavaScript"></script>
<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"
@ -119,44 +119,97 @@
action="/WebGoat/JWT/refresh/reset"
enctype="application/json;charset=UTF-8">
<div class="container-fluid">
<div class="row">
<div class="well">
<div class="pull-right">
<div class="dropdown">
<button type="button" data-toggle="dropdown" class="btn btn-default dropdown-toggle"
title="Change user">
<i class="fa fa-user"></i> <span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-left">
<li role="presentation"><a role="menuitem" tabindex="-1"
onclick="javascript:login('Guest')"
th:text="Guest">current</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1"
onclick="javascript:login('Tom')"
th:text="Tom">current</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1"
onclick="javascript:login('Jerry')"
th:text="Jerry">current</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1"
onclick="javascript:login('Sylvester')"
th:text="Sylvester">current</a></li>
</ul>
<button type="button" class="btn btn-default fa fa-refresh" title="Refresh votes"
onclick="javascript:getVotings()"/>
<button type="submit" class="btn btn-default fa fa-trash-o" title="Reset votes"/>
<div class="col-sm-6 col-md-4 col-lg-3 mt-4">
<div class="card card-inverse card-info">
<img th:src="@{/images/jerry.png}" class="card-img-top"></img>
<div class="card-block">
<figure class="profile profile-inline">
<img th:src="@{/images/jerry.png}" class="profile-avatar" alt=""></img>
</figure>
<h4 class="card-title">Jerry</h4>
<div class="card-text">
Jerry is a small, brown, house mouse.
</div>
<div>
<p class="text-right">Welcome back, <b><span id="name"></span></b></p>
</div>
<div class="card-footer">
<small>Last updated 12 minutes ago</small>
<button class="btn btn-info float-right btn-sm">Follow</button>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3 mt-4">
<div class="card card-inverse card-info">
<img th:src="@{/images/tom.png}" class="card-img-top"></img>
<div class="card-block">
<figure class="profile profile-inline">
<img th:src="@{/images/tom.png}" class="profile-avatar" alt=""></img>
</figure>
<h4 class="card-title">Tom</h4>
<div class="card-text">
Tom is a grey and white domestic short hair cat.
</div>
</div>
<div class="card-footer">
<small>Last updated 12 days ago</small>
<button class="btn btn-info float-right btn-sm">Follow</button>
</div>
</div>
</div>
</div>
</form>
<br/>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
<div>
<h3>Vote for your favorite</h3>
</div>
<div id="votesList" class="list-group">
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:JWT_final.adoc"></div>
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/jwt.css}"/>
<script th:src="@{/lesson_js/bootstrap.min.js}" language="JavaScript"></script>
<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"
method="POST"
action="/WebGoat/JWT/final/delete?token=eyJ0eXAiOiJKV1QiLCJraWQiOiJ3ZWJnb2F0X2tleSIsImFsZyI6IkhTMjU2In0.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJpYXQiOjE1MjQyMTA5MDQsImV4cCI6MTYxODkwNTMwNCwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJqZXJyeUB3ZWJnb2F0LmNvbSIsInVzZXJuYW1lIjoiSmVycnkiLCJFbWFpbCI6ImplcnJ5QHdlYmdvYXQuY29tIiwiUm9sZSI6WyJDYXQiXX0.CgZ27DzgVW8gzc0n6izOU638uUCi6UhiOJKYzoEZGE8"
enctype="application/json;charset=UTF-8">
<div class="container-fluid">
<div class="col-sm-6 col-md-4 col-lg-3 mt-4">
<div class="card card-inverse card-info">
<img th:src="@{/images/jerry.png}" class="card-img-top"></img>
<div class="card-block">
<figure class="profile profile-inline">
<img th:src="@{/images/jerry.png}" class="profile-avatar" alt=""></img>
</figure>
<h4 class="card-title">Jerry</h4>
<div class="card-text">
Jerry is a small, brown, house mouse.
</div>
</div>
<div class="card-footer">
<small>Last updated 12 minutes ago</small>
<button class="btn btn-info float-right btn-sm">Delete</button>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3 mt-4">
<div class="card card-inverse card-info">
<img th:src="@{/images/tom.png}" class="card-img-top"></img>
<div class="card-block">
<figure class="profile profile-inline">
<img th:src="@{/images/tom.png}" class="profile-avatar" alt=""></img>
</figure>
<h4 class="card-title">Tom</h4>
<div class="card-text">
Tom is a grey and white domestic short hair cat.
</div>
</div>
<div class="card-footer">
<small>Last updated 12 days ago</small>
<button class="btn btn-info float-right btn-sm" onclick="javascript:follow('Tom')">Follow</button>
<button class="btn btn-info float-right btn-sm">Delete</button>
</div>
</div>
</div>

View File

@ -13,3 +13,13 @@ jwt-change-token-hint5=Submit the token by changing the algorithm to None and re
jwt-secret-hint1=Save the token and try to verify the token locally
jwt-secret-hint2=Download a word list dictionary (https://github.com/first20hours/google-10000-english)
jwt-secret-hint3=Write a small program or use HashCat for brute forcing the token according the word list
jwt-final-jerry-account=Yikes, you are removing Jerry's account, try to delete the account of Tom
jwt-final-not-tom=Username is not Tom try to pass a token for Tom
jwt-final-hint1=Take a look at the token and specifically and the header
jwt-final-hint2=The 'kid' (key ID) header parameter is a hint indicating which key was used to secure the JWS
jwt-final-hint3=The key can be located on the filesystem in memory or even reside in the database
jwt-final-hint4=The key is stored in the database and loaded while verifying a token
jwt-final-hint5=Using a SQL injection you might be able to manipulate the key to something you know and create a new token.
jwt-final-hint6=Use: key1' union all select 'abcdefg' limit 1,1 -- And change the contents of the token to Tom and hit the endpoint with the new token

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

View File

@ -0,0 +1,6 @@
205.167.170.15 - - [28/Jan/2016:21:28:01 +0100] "GET /statuses/605086114483826688/favorite?authenticity_token=264c8bf11e0212227c001b77543c3519 HTTP/1.1" 404 242 "-" "Go-http-client/1.1" "-"
205.167.170.15 - - [28/Jan/2016:21:28:01 +0100] "GET /images/phocagallery/almhuette/thumbs/phoca_thumb_l_zimmer.jpg HTTP/1.1" 200 12783 "-" "Go-http-client/1.1" "-"
205.167.170.15 - - [28/Jan/2016:21:28:01 +0100] "GET /signup HTTP/1.1" 404 212 "-" "Go-http-client/1.1" "-"
205.167.170.15 - - [28/Jan/2016:21:28:01 +0100] "GET /vsmartseo/status/605086114483826688/actions HTTP/1.1" 404 249 "-" "Go-http-client/1.1" "-"
205.167.170.15 - - [28/Jan/2016:21:28:01 +0100] "GET /vsmartseo?p=s HTTP/1.1" 404 215 "-" "Go-http-client/1.1" "-"

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

View File

@ -0,0 +1,10 @@
function follow(user) {
$.ajax({
type: 'POST',
url: 'JWT/final/follow/' + user
}).then(function (result) {
$("#toast").setTextContent(result);
})
}
}

View File

@ -0,0 +1,5 @@
== Final challenges
Same as before try to change the token and become admin, this assignment requires multiple attack scenarios.

View File

@ -2,7 +2,7 @@
=== Assignment
In this assignment Tom no longer has a valid token, try to find a way to create a new access token and use that token
to vote.
Jerry really wants to follow Tom, can you help him to follow Jerry?
From a breach of last year the following logfile is available link:images/logs.txt[here]

View File

@ -13,7 +13,7 @@ to be aware of before validating the token.
== Assignment
Try to change the token you receive and become an admin user by changing the token.
Try to change the token you receive and become an admin user by changing the token and once you are admin reset the votes