diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/session/CreateDB.java b/webgoat-container/src/main/java/org/owasp/webgoat/session/CreateDB.java index a6743024f..47545ef35 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/session/CreateDB.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/session/CreateDB.java @@ -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."); } } diff --git a/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/plugin/JWTFinalEndpoint.java b/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/plugin/JWTFinalEndpoint.java new file mode 100644 index 000000000..1dd765faf --- /dev/null +++ b/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/plugin/JWTFinalEndpoint.java @@ -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()); + } + } + } +} diff --git a/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/plugin/refresh/RefreshEndpoint.java b/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/plugin/refresh/RefreshEndpoint.java new file mode 100644 index 000000000..d04cf8ef7 --- /dev/null +++ b/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/plugin/refresh/RefreshEndpoint.java @@ -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"; + + +} diff --git a/webgoat-lessons/jwt/src/main/resources/css/jwt.css b/webgoat-lessons/jwt/src/main/resources/css/jwt.css index 590e2a4b0..19dd769d0 100644 --- a/webgoat-lessons/jwt/src/main/resources/css/jwt.css +++ b/webgoat-lessons/jwt/src/main/resources/css/jwt.css @@ -9,4 +9,108 @@ 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); } \ No newline at end of file diff --git a/webgoat-lessons/jwt/src/main/resources/html/JWT.html b/webgoat-lessons/jwt/src/main/resources/html/JWT.html index e50b9a405..bd2326f0d 100644 --- a/webgoat-lessons/jwt/src/main/resources/html/JWT.html +++ b/webgoat-lessons/jwt/src/main/resources/html/JWT.html @@ -106,11 +106,11 @@
-
+
- +
- -
- -
-
- -
-

Welcome back,

+
+
+ +
+
+ +
+

Jerry

+
+ Jerry is a small, brown, house mouse.
- -
-

Vote for your favorite

+ -
+
+
+
+
+ +
+
+ +
+

Tom

+
+ Tom is a grey and white domestic short hair cat. +
+
+ +
+
+
+ +
+
+
+
+
+ +
+
+ + + +
+
+
+
+
+
+ +
+
+ +
+

Jerry

+
+ Jerry is a small, brown, house mouse. +
+
+ +
+
+
+
+ +
+
+ +
+

Tom

+
+ Tom is a grey and white domestic short hair cat. +
+
+
diff --git a/webgoat-lessons/jwt/src/main/resources/i18n/WebGoatLabels.properties b/webgoat-lessons/jwt/src/main/resources/i18n/WebGoatLabels.properties index 3137cc27b..ff7ea9f7c 100644 --- a/webgoat-lessons/jwt/src/main/resources/i18n/WebGoatLabels.properties +++ b/webgoat-lessons/jwt/src/main/resources/i18n/WebGoatLabels.properties @@ -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 \ No newline at end of file diff --git a/webgoat-lessons/jwt/src/main/resources/images/jerry.png b/webgoat-lessons/jwt/src/main/resources/images/jerry.png new file mode 100644 index 000000000..5ed492711 Binary files /dev/null and b/webgoat-lessons/jwt/src/main/resources/images/jerry.png differ diff --git a/webgoat-lessons/jwt/src/main/resources/images/logs.txt b/webgoat-lessons/jwt/src/main/resources/images/logs.txt new file mode 100644 index 000000000..a4d1c7107 --- /dev/null +++ b/webgoat-lessons/jwt/src/main/resources/images/logs.txt @@ -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" "-" diff --git a/webgoat-lessons/jwt/src/main/resources/images/tom.png b/webgoat-lessons/jwt/src/main/resources/images/tom.png new file mode 100644 index 000000000..d8e50d91a Binary files /dev/null and b/webgoat-lessons/jwt/src/main/resources/images/tom.png differ diff --git a/webgoat-lessons/jwt/src/main/resources/js/jwt-final.js b/webgoat-lessons/jwt/src/main/resources/js/jwt-final.js new file mode 100644 index 000000000..7d3cc2c41 --- /dev/null +++ b/webgoat-lessons/jwt/src/main/resources/js/jwt-final.js @@ -0,0 +1,10 @@ +function follow(user) { + $.ajax({ + type: 'POST', + url: 'JWT/final/follow/' + user + }).then(function (result) { + $("#toast").setTextContent(result); + }) +} +} + diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_final.adoc b/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_final.adoc new file mode 100644 index 000000000..3cd423952 --- /dev/null +++ b/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_final.adoc @@ -0,0 +1,5 @@ +== Final challenges + +Same as before try to change the token and become admin, this assignment requires multiple attack scenarios. + + diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_refresh_assignment.adoc b/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_refresh_assignment.adoc index 8690dd64d..87f787a0f 100644 --- a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_refresh_assignment.adoc +++ b/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_refresh_assignment.adoc @@ -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] diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_signing.adoc b/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_signing.adoc index 12bdc93a9..0d526de1c 100644 --- a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_signing.adoc +++ b/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_signing.adoc @@ -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