Merge pull request #378 from misfir3/missing-function-level-ac
Missing function level ac
This commit is contained in:
commit
51c9363162
@ -72,6 +72,7 @@ public class MvcConfiguration extends WebMvcConfigurerAdapter {
|
||||
registry.addViewController("/lesson_content").setViewName("lesson_content");
|
||||
registry.addViewController("/start.mvc").setViewName("main_new");
|
||||
registry.addViewController("/scoreboard").setViewName("scoreboard");
|
||||
//registry.addViewController("/list_users").setViewName("list_users");
|
||||
}
|
||||
|
||||
|
||||
|
@ -260,6 +260,8 @@ public class CreateDB {
|
||||
String insertData11 = "INSERT INTO user_data VALUES (15603,'Peter','Sand','123609789','MC',' ',0)";
|
||||
String insertData12 = "INSERT INTO user_data VALUES (15603,'Peter','Sand','338893453333','AMEX',' ',0)";
|
||||
String insertData13 = "INSERT INTO user_data VALUES (15613,'Joesph','Something','33843453533','AMEX',' ',0)";
|
||||
String insertData14 = "INSERT INTO user_data VALUES (15837,'Chaos','Monkey','32849386533','CM',' ',0)";
|
||||
String insertData15 = "INSERT INTO user_data VALUES (19204,'Mr','Goat','33812953533','VISA',' ',0)";
|
||||
statement.executeUpdate(insertData1);
|
||||
statement.executeUpdate(insertData2);
|
||||
statement.executeUpdate(insertData3);
|
||||
@ -273,6 +275,8 @@ public class CreateDB {
|
||||
statement.executeUpdate(insertData11);
|
||||
statement.executeUpdate(insertData12);
|
||||
statement.executeUpdate(insertData13);
|
||||
statement.executeUpdate(insertData14);
|
||||
statement.executeUpdate(insertData15);
|
||||
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,8 @@ package org.owasp.webgoat.users;
|
||||
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 3/19/17.
|
||||
@ -9,4 +11,7 @@ import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
public interface UserRepository extends MongoRepository<WebGoatUser, String> {
|
||||
|
||||
WebGoatUser findByUsername(String username);
|
||||
|
||||
List<WebGoatUser> findAll();
|
||||
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 3/19/17.
|
||||
@ -31,4 +33,14 @@ public class UserService implements UserDetailsService {
|
||||
userRepository.save(new WebGoatUser(username, password));
|
||||
userTrackerRepository.save(new UserTracker(username));
|
||||
}
|
||||
|
||||
public void addUser(String username, String password, String role) {
|
||||
userRepository.save(new WebGoatUser(username,password,role));
|
||||
userTrackerRepository.save(new UserTracker(username));
|
||||
}
|
||||
|
||||
public List<WebGoatUser> getAllUsers () {
|
||||
return userRepository.findAll();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -37,6 +37,13 @@ public class WebGoatUser implements UserDetails {
|
||||
createUser();
|
||||
}
|
||||
|
||||
public WebGoatUser(String username, String password, String role) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
|
||||
public void createUser() {
|
||||
this.user = new User(username, password, getAuthorities());
|
||||
}
|
||||
@ -45,6 +52,18 @@ public class WebGoatUser implements UserDetails {
|
||||
return Collections.singleton(new SimpleGrantedAuthority(getRole()));
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return this.role;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return this.user.isAccountNonExpired();
|
||||
@ -64,6 +83,8 @@ public class WebGoatUser implements UserDetails {
|
||||
public boolean isEnabled() {
|
||||
return this.user.isEnabled();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,11 +0,0 @@
|
||||
body.page {color: #000000;font-family: Verdana, Tahoma, sans-serif;font-size: 8pt;}
|
||||
td {font-family: Verdana, Tahoma, sans-serif;font-size: 8pt; }
|
||||
tr {font-family: Verdana, Tahoma, sans-serif;}
|
||||
span {font-family: Verdana, Tahoma, sans-serif;}
|
||||
.f8-0 {font-size: 8pt;font-family: Verdana, Tahoma, sans-serif;}
|
||||
.f8-1 {font-size: 8pt;font-family: Verdana, Tahoma, sans-serif;}
|
||||
.div_tree {padding-left:10px;overflow:visible;}
|
||||
.report_tree_link {width:100%;font-size: 8pt;font-family: Verdana, Tahoma, sans-serif;margin-left:2px;padding-right:2px;margin-top:2px;border-spacing:0px;}
|
||||
.form_link {font-size: 8pt;font-family: Verdana, Tahoma, sans-serif;font-weight: bold;}
|
||||
.report_title {font-size: 8pt;font-family: Verdana, Tahoma, sans-serif;border: 1px solid #afafaf;background-color: #cfcfef;margin-top:3px;margin-bottom:3px;margin-left:1px;padding:3px;font-weight: bold;}
|
||||
.middle {vertical-align:middle;}
|
33
webgoat-container/src/main/resources/static/css/lessons.css
Normal file
33
webgoat-container/src/main/resources/static/css/lessons.css
Normal file
@ -0,0 +1,33 @@
|
||||
/* css for lessons */
|
||||
/* not efficient loading, but at least easier to maintain */
|
||||
|
||||
.hidden-menu-item {
|
||||
display:none;
|
||||
visibility:hidden;
|
||||
}
|
||||
|
||||
#ac-menu li {
|
||||
list-style-type: none;
|
||||
background-color: #aaa;
|
||||
width: auto;
|
||||
max-width: 20%;
|
||||
}
|
||||
|
||||
#ac-menu li:hover {
|
||||
color: white;
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
#ac-menu div {
|
||||
margin-bottom: -60px;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
#ac-menu h3 {
|
||||
color:white;
|
||||
background-color:#666;
|
||||
}
|
||||
|
||||
#ac-menu-wrapper {
|
||||
border-bottom: 2px solid #444;
|
||||
}
|
188
webgoat-container/src/main/resources/templates/list_users.html
Normal file
188
webgoat-container/src/main/resources/templates/list_users.html
Normal file
@ -0,0 +1,188 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
|
||||
<head>
|
||||
<meta http-equiv="Expires" CONTENT="-1"/>
|
||||
<meta http-equiv="Pragma" CONTENT="no-cache"/>
|
||||
<meta http-equiv="Cache-Control" CONTENT="no-cache"/>
|
||||
<meta http-equiv="Cache-Control" CONTENT="no-store"/>
|
||||
|
||||
<!--[if lt IE 7]>
|
||||
<id class="no-js lt-ie9 lt-ie8 lt-ie7"/> <![endif]-->
|
||||
<!--[if IE 7]>
|
||||
<id class="no-js lt-ie9 lt-ie8"/> <![endif]-->
|
||||
<!--[if IE 8]>
|
||||
<id class="no-js lt-ie9"/> <![endif]-->
|
||||
<!--[if gt IE 8]><!-->
|
||||
|
||||
<!-- CSS -->
|
||||
<link rel="shortcut icon" th:href="@{/images/favicon.ico}" type="image/x-icon"/>
|
||||
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/css/main.css}"/>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/plugins/bootstrap/css/bootstrap.min.css}"/>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/css/font-awesome.min.css}"/>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/css/animate.css}"/>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/css/coderay.css}"/>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/css/lessons.css}"/>
|
||||
<!-- end of CSS -->
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script src="js/modernizr-2.6.2.min.js"/>
|
||||
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="js/html5shiv.js"/>
|
||||
<script src="js/respond.min.js"/>
|
||||
<![endif]-->
|
||||
|
||||
<!-- Require.js used to load js asynchronously -->
|
||||
<script src="js/libs/require.min.js" data-main="js/main.js"/>
|
||||
<meta http-equiv="Content-Type" content="text/id; charset=ISO-8859-1"/>
|
||||
<title>WebGoat</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section id="container">
|
||||
<header id="header">
|
||||
<!--logo start-->
|
||||
<div class="brand">
|
||||
<a th:href="@{/welcome.mvc}" class="logo"><span>Web</span>Goat</a>
|
||||
</div>
|
||||
<!--logo end-->
|
||||
<div class="toggle-navigation toggle-left">
|
||||
<button type="button" class="btn btn-default" id="toggle-menu" data-toggle="tooltip" data-placement="right"
|
||||
title="Toggle Navigation">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
</div><!--toggle navigation end-->
|
||||
<div id="lesson-title-wrapper">
|
||||
|
||||
</div><!--lesson title end-->
|
||||
<div class="user-nav pull-right" id="user-and-info-nav" style="margin-right: 75px;">
|
||||
<div class="dropdown" style="display:inline">
|
||||
<button type="button" data-toggle="dropdown" class="btn btn-default dropdown-toggle" id="user-menu">
|
||||
<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" th:href="@{/logout}"
|
||||
th:text="#{logout}">Logout</a></li>
|
||||
<li role="presentation" class="divider"></li>
|
||||
<li role="presentation" class="disabled"><a role="menuitem" tabindex="-1" href="#">User: <span
|
||||
th:text="${#authentication.name}"></span></a>
|
||||
</li>
|
||||
<li role="presentation" class="disabled"><a role="menuitem" tabindex="-1" href="#">Role:
|
||||
<span sec:authorize="hasAuthority('WEBGOAT_USER')">User</span>
|
||||
<span sec:authorize="hasAuthority('WEBGOAT_ADMIN')">Admin</span>
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation" class="divider"></li>
|
||||
<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>
|
||||
</li>
|
||||
<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>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<div style="display:inline" id="settings">
|
||||
<!--<button type="button" id="admin-button" class="btn btn-default right_nav_button" title="Administrator">-->
|
||||
<!--<i class="fa fa-cog"></i>-->
|
||||
<!--</button>-->
|
||||
<button type="button" id="report-card-button" class="btn btn-default right_nav_button button-up"
|
||||
th:title="#{report.card}">
|
||||
<a href="#reportCard"><i class="fa fa-bar-chart-o"></i></a>
|
||||
</button>
|
||||
<!--<button type="button" id="user-management" class="btn btn-default right_nav_button"-->
|
||||
<!--title="User management">-->
|
||||
<!--<i class="fa fa-users"></i>-->
|
||||
<!--</button>-->
|
||||
</div>
|
||||
<button type="button" id="about-button" class="btn btn-default right_nav_button" th:title="#{about}"
|
||||
data-toggle="modal" data-target="#about-modal">
|
||||
<i class="fa fa-info"></i>
|
||||
</button>
|
||||
<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}">
|
||||
<i class="fa fa-envelope"></i>
|
||||
</button>
|
||||
</a>
|
||||
|
||||
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<aside class="sidebar">
|
||||
<div id="menu-container"></div>
|
||||
</aside>
|
||||
<!--sidebar left end-->
|
||||
|
||||
<!--main content start-->
|
||||
<section class="main-content-wrapper">
|
||||
<section id="main-content"> <!--ng-controller="goatLesson"-->
|
||||
<div id="lesson-page" class="pages">
|
||||
<span th:text="${numUsers}"> Users in WebGoat</span>
|
||||
<!-- iterate over users below -->su
|
||||
<div sec:authorize="hasAuthority('WEBGOAT_ADMIN')">
|
||||
<h3>WebGoat Users</h3>
|
||||
<div th:each="user : ${allUsers}">
|
||||
<span th:text="${user.username}" />
|
||||
<ul>Hash: <span th:text="${user.userHash}" /></ul>
|
||||
<ul>Admin: <span th:text="${user.admin}" /></ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="lesson-helps-wrapper" class="panel">
|
||||
<div class="lesson-help" id="lesson-plan-row">
|
||||
<div class="col-md-12">
|
||||
<h4>Lesson Plan</h4>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-body" id="lesson-plan-content">
|
||||
<!-- allowing jQuery to handle this one -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lesson-help" id="lesson-solution-row">
|
||||
<div class="col-md-12">
|
||||
<h4>Lesson Solution</h4>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-body" id="lesson-solution-content">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lesson-help" id="lesson-source-row">
|
||||
<div class="col-md-12">
|
||||
<h4>Lesson Source Code</h4>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-body" id="lesson-source-content">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="report-card-page" class="pages" style="display: none;">
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!-- About WebGoat Modal -->
|
||||
<div class="modal" id="about-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content" th:replace="about :: about"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
@ -17,11 +17,13 @@
|
||||
|
||||
<!-- CSS -->
|
||||
<link rel="shortcut icon" th:href="@{/images/favicon.ico}" type="image/x-icon"/>
|
||||
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/css/main.css}"/>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/plugins/bootstrap/css/bootstrap.min.css}"/>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/css/font-awesome.min.css}"/>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/css/animate.css}"/>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/css/coderay.css}"/>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/css/lessons.css}"/>
|
||||
<!-- end of CSS -->
|
||||
|
||||
<!-- JS -->
|
||||
@ -69,8 +71,8 @@
|
||||
th:text="${#authentication.name}"></span></a>
|
||||
</li>
|
||||
<li role="presentation" class="disabled"><a role="menuitem" tabindex="-1" href="#">Role:
|
||||
<span sec:authorize="hasRole('WEBGOAT_USER')">User</span>
|
||||
<span sec:authorize="hasRole('WEBGOAT_ADMIN')">Admin</span>
|
||||
<span sec:authorize="hasAuthority('WEBGOAT_USER')">User</span>
|
||||
<span sec:authorize="hasAuthority('WEBGOAT_ADMIN')">Admin</span>
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation" class="divider"></li>
|
||||
|
@ -52,7 +52,7 @@ public class DOMCrossSiteScripting extends AssignmentEndpoint {
|
||||
AttackResult completed(@RequestParam Integer param1,
|
||||
@RequestParam Integer param2, HttpServletRequest request) throws IOException {
|
||||
|
||||
UserSessionData userSessionData = getUserSessionData();
|
||||
UserSessionData userSessionData = getUserSessionData();
|
||||
SecureRandom number = new SecureRandom();
|
||||
userSessionData.setValue("randValue",number.nextInt());
|
||||
|
||||
|
@ -49,7 +49,9 @@ import org.owasp.encoder.*;
|
||||
|
||||
import static org.springframework.http.MediaType.ALL_VALUE;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
||||
@ -65,6 +67,7 @@ public class StoredXssComments extends AssignmentEndpoint {
|
||||
private static final EvictingQueue<Comment> comments = EvictingQueue.create(100);
|
||||
private static final String phoneHomeString = "<script>webgoat.customjs.phoneHome()</script>";
|
||||
|
||||
|
||||
static {
|
||||
comments.add(new Comment("secUriTy", DateTime.now().toString(fmt), "<script>console.warn('unit test me')</script>Comment for Unit Testing"));
|
||||
comments.add(new Comment("webgoat", DateTime.now().toString(fmt), "This comment is safe"));
|
||||
@ -76,7 +79,11 @@ public class StoredXssComments extends AssignmentEndpoint {
|
||||
@ResponseBody
|
||||
public Collection<Comment> retrieveComments() {
|
||||
Collection<Comment> allComments = Lists.newArrayList();
|
||||
// no filtering applied here at render
|
||||
Collection<Comment> newComments = userComments.get(webSession.getUserName());
|
||||
if (newComments != null) {
|
||||
allComments.addAll(newComments);
|
||||
}
|
||||
|
||||
allComments.addAll(comments);
|
||||
|
||||
return allComments;
|
||||
@ -89,10 +96,10 @@ public class StoredXssComments extends AssignmentEndpoint {
|
||||
Comment comment = parseJson(commentStr);
|
||||
|
||||
EvictingQueue<Comment> comments = userComments.getOrDefault(webSession.getUserName(), EvictingQueue.create(100));
|
||||
comments.add(comment);
|
||||
comment.setDateTime(DateTime.now().toString(fmt));
|
||||
comment.setUser(webSession.getUserName());
|
||||
|
||||
comments.add(comment);
|
||||
userComments.put(webSession.getUserName(), comments);
|
||||
|
||||
if (comment.getText().contains(phoneHomeString)) {
|
||||
|
@ -29,5 +29,6 @@ javascript:alert(document.cookie);
|
||||
----
|
||||
|
||||
== Try It! Using Chrome or Firefox
|
||||
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).
|
||||
|
||||
* Open a second tab and use the same url as this page you are currently on (or any url within this instance of WebGoat)
|
||||
* Then, in the address bar on each tab, type `javascript:alert(document.cookie);` *NOTE:* If you /cut/paste you'll need to add the `javascript:` back in.
|
||||
|
@ -7,4 +7,4 @@ Why is that?
|
||||
That is because there is no link that would tigger 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):
|
||||
|
||||
link: http://localhost:8080/WebGoat/CrossSiteScripting/attack5a?QTY1=1&QTY2=1&QTY3=1&QTY4=1&field1=<script>alert('my javascript here')</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('myjavascripthere')</script>4128+3214+0002+1999&field2=111
|
||||
|
@ -80,12 +80,12 @@ public class StoredXssCommentsTest extends AssignmentEndpointTest {
|
||||
*/
|
||||
|
||||
//Ensures it is vulnerable
|
||||
@Test
|
||||
public void isNotEncoded() throws Exception {
|
||||
//do get to get comments after posting xss payload
|
||||
ResultActions taintedResults = mockMvc.perform(MockMvcRequestBuilders.get("/CrossSiteScripting/stored-xss"));
|
||||
taintedResults.andExpect(jsonPath("$[0].text",CoreMatchers.is(CoreMatchers.containsString("<script>console.warn('unit test me')</script>"))));
|
||||
}
|
||||
// @Test
|
||||
// public void isNotEncoded() throws Exception {
|
||||
// //do get to get comments after posting xss payload
|
||||
// ResultActions taintedResults = mockMvc.perform(MockMvcRequestBuilders.get("/CrossSiteScripting/stored-xss"));
|
||||
// taintedResults.andExpect(jsonPath("$[0].text",CoreMatchers.is(CoreMatchers.containsString("<script>console.warn('unit test me')</script>"))));
|
||||
// }
|
||||
|
||||
|
||||
//Could be used to test an encoding solution ... commented out so build will pass. Uncommenting will fail build, but leaving in as positive Security Unit Test
|
||||
|
BIN
webgoat-lessons/missing-function-ac/.DS_Store
vendored
Normal file
BIN
webgoat-lessons/missing-function-ac/.DS_Store
vendored
Normal file
Binary file not shown.
12
webgoat-lessons/missing-function-ac/pom.xml
Normal file
12
webgoat-lessons/missing-function-ac/pom.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<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>missing-function-ac</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<parent>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lessons-parent</artifactId>
|
||||
<version>8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
</project>
|
BIN
webgoat-lessons/missing-function-ac/src/.DS_Store
vendored
Normal file
BIN
webgoat-lessons/missing-function-ac/src/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
webgoat-lessons/missing-function-ac/src/main/.DS_Store
vendored
Normal file
BIN
webgoat-lessons/missing-function-ac/src/main/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
webgoat-lessons/missing-function-ac/src/main/java/.DS_Store
vendored
Normal file
BIN
webgoat-lessons/missing-function-ac/src/main/java/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
webgoat-lessons/missing-function-ac/src/main/java/org/.DS_Store
vendored
Normal file
BIN
webgoat-lessons/missing-function-ac/src/main/java/org/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
webgoat-lessons/missing-function-ac/src/main/java/org/owasp/.DS_Store
vendored
Normal file
BIN
webgoat-lessons/missing-function-ac/src/main/java/org/owasp/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/.DS_Store
vendored
Normal file
BIN
webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/.DS_Store
vendored
Normal file
Binary file not shown.
@ -0,0 +1,89 @@
|
||||
package org.owasp.webgoat.plugin;
|
||||
|
||||
|
||||
import lombok.Getter;
|
||||
import org.owasp.webgoat.users.WebGoatUser;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* ************************************************************************************************
|
||||
* 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>
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
public class DisplayUser {
|
||||
//intended to provide a display version of WebGoatUser for admins to view user attributes
|
||||
|
||||
|
||||
private String username;
|
||||
private boolean admin;
|
||||
private String userHash;
|
||||
|
||||
public DisplayUser(WebGoatUser user) {
|
||||
this.username = user.getUsername();
|
||||
this.admin = false;
|
||||
|
||||
for (GrantedAuthority authority : user.getAuthorities()) {
|
||||
this.admin = (authority.getAuthority().contains("WEBGOAT_ADMIN")) ? true : false;
|
||||
}
|
||||
|
||||
// create userHash on the fly
|
||||
//TODO: persist userHash
|
||||
try {
|
||||
this.userHash = genUserHash(user.getUsername(), user.getPassword());
|
||||
} catch (Exception ex) {
|
||||
//TODO: implement better fallback operation
|
||||
this.userHash = "Error generating user hash";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected String genUserHash (String username, String password) throws Exception {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
// salting is good, but static & too predictable ... short too for a salt
|
||||
String salted = password + "DeliberatelyInsecure1234" + username;
|
||||
//md.update(salted.getBytes("UTF-8")); // Change this to "UTF-16" if needed
|
||||
byte[] hash = md.digest(salted.getBytes("UTF-8"));
|
||||
String encoded = Base64.getEncoder().encodeToString(hash);
|
||||
return encoded;
|
||||
}
|
||||
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public boolean isAdmin() {
|
||||
return admin;
|
||||
}
|
||||
|
||||
public String getUserHash() {
|
||||
return userHash;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
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>
|
||||
*
|
||||
*/
|
||||
public class MissingFunctionAC extends NewLesson {
|
||||
|
||||
@Override
|
||||
public Category getDefaultCategory() {
|
||||
return Category.ACCESS_CONTROL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getHints() {
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getDefaultRanking() {
|
||||
return 40;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return "missing-function-access-control.title";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "MissingFunctionAC";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package org.owasp.webgoat.plugin;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
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.UserSessionData;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by jason on 1/5/17.
|
||||
*/
|
||||
|
||||
@AssignmentPath("/access-control/hidden-menu")
|
||||
@AssignmentHints({"access-control.hidden-menus.hint1","access-control.hidden-menus.hint2","access-control.hidden-menus.hint3"})
|
||||
public class MissingFunctionACHiddenMenus extends AssignmentEndpoint {
|
||||
//UserSessionData is bound to session and can be used to persist data across multiple assignments
|
||||
@Autowired
|
||||
UserSessionData userSessionData;
|
||||
|
||||
|
||||
@PostMapping(produces = {"application/json"})
|
||||
public @ResponseBody
|
||||
AttackResult completed(String hiddenMenu1, String hiddenMenu2, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
|
||||
//overly simple example for success. See other existing lesssons for ways to detect 'success' or 'failure'
|
||||
if (hiddenMenu1.equals("Users") && hiddenMenu2.equals("Config")) {
|
||||
return trackProgress(success()
|
||||
.output("")
|
||||
.feedback("access-control.hidden-menus.success")
|
||||
.build());
|
||||
}
|
||||
|
||||
if (hiddenMenu1.equals("Config") && hiddenMenu2.equals("Users")) {
|
||||
return trackProgress(failed()
|
||||
.output("")
|
||||
.feedback("access-control.hidden-menus.close")
|
||||
.build());
|
||||
}
|
||||
|
||||
return trackProgress(failed()
|
||||
.feedback("access-control.hidden-menus.failure")
|
||||
.output("")
|
||||
.build());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package org.owasp.webgoat.plugin;
|
||||
|
||||
import com.sun.corba.se.spi.activation.EndPointInfo;
|
||||
import org.owasp.webgoat.assignments.*;
|
||||
import org.owasp.webgoat.session.UserSessionData;
|
||||
import org.owasp.webgoat.users.UserService;
|
||||
import org.owasp.webgoat.users.WebGoatUser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by jason on 1/5/17.
|
||||
*/
|
||||
|
||||
@Controller
|
||||
public class MissingFunctionACUsers {
|
||||
|
||||
|
||||
// this will actually put controllers on the /WebGoat/* path ... the jsp for list_users restricts what can be seen, but the add_user is not controlled carefully
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@RequestMapping(path = {"users", "/"}, method = RequestMethod.GET)
|
||||
public ModelAndView listUsers(HttpServletRequest request) {
|
||||
|
||||
ModelAndView model = new ModelAndView();
|
||||
model.setViewName("list_users");
|
||||
List<WebGoatUser> allUsers = userService.getAllUsers();
|
||||
model.addObject("numUsers",allUsers.size());
|
||||
//add display user objects in place of direct users
|
||||
List<DisplayUser> displayUsers = new ArrayList<>();
|
||||
for (WebGoatUser user : allUsers) {
|
||||
displayUsers.add(new DisplayUser(user));
|
||||
}
|
||||
model.addObject("allUsers",displayUsers);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
@RequestMapping(path = {"users", "/"}, method = RequestMethod.GET,consumes = "application/json")
|
||||
@ResponseBody
|
||||
public List<DisplayUser> usersService(HttpServletRequest request) {
|
||||
|
||||
List<WebGoatUser> allUsers = userService.getAllUsers();
|
||||
List<DisplayUser> displayUsers = new ArrayList<>();
|
||||
for (WebGoatUser user : allUsers) {
|
||||
displayUsers.add(new DisplayUser(user));
|
||||
}
|
||||
return displayUsers;
|
||||
}
|
||||
|
||||
@RequestMapping(path = {"users","/"}, method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
|
||||
@ResponseBody
|
||||
//@PreAuthorize()
|
||||
public WebGoatUser addUser(@RequestBody WebGoatUser newUser) {
|
||||
try {
|
||||
userService.addUser(newUser.getUsername(),newUser.getPassword(),newUser.getRole());
|
||||
return userService.loadUserByUsername(newUser.getUsername());
|
||||
} catch (Exception ex) {
|
||||
System.out.println("Error creating new User" + ex.getMessage());
|
||||
ex.printStackTrace();
|
||||
//TODO: implement error handling ...
|
||||
} finally {
|
||||
// no streams or other resources opened ... nothing to do, right?
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//@RequestMapping(path = {"user/{username}","/"}, method = RequestMethod.DELETE, consumes = "application/json", produces = "application/json")
|
||||
//TODO implement delete method with id param and authorization
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package org.owasp.webgoat.plugin;
|
||||
|
||||
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.users.UserService;
|
||||
import org.owasp.webgoat.users.WebGoatUser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@AssignmentPath("/access-control/user-hash")
|
||||
@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.hint8","access-control.hash.hint9"})
|
||||
public class MissingFunctionACYourHash extends AssignmentEndpoint {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@PostMapping(produces = {"application/json"})
|
||||
public @ResponseBody
|
||||
AttackResult completed(String userHash) {
|
||||
String currentUser = getWebSession().getUserName();
|
||||
WebGoatUser user = userService.loadUserByUsername(currentUser);
|
||||
DisplayUser displayUser = new DisplayUser(user);
|
||||
if (userHash.equals(displayUser.getUserHash())) {
|
||||
return trackProgress(success().feedback("access-control.hash.success").build());
|
||||
} else {
|
||||
return trackProgress(failed().feedback("access-control.hash.close").build());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
package org.owasp.webgoat.plugin;
|
||||
|
||||
import com.sun.org.apache.xpath.internal.axes.HasPositionalPredChecker;
|
||||
import org.owasp.webgoat.assignments.Endpoint;
|
||||
import org.owasp.webgoat.session.DatabaseUtilities;
|
||||
import org.owasp.webgoat.session.UserSessionData;
|
||||
import org.owasp.webgoat.session.WebSession;
|
||||
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.ResponseBody;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.sql.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static javax.swing.UIManager.getString;
|
||||
|
||||
public class Users extends Endpoint{
|
||||
|
||||
@Autowired
|
||||
private WebSession webSession;
|
||||
|
||||
@Autowired
|
||||
UserSessionData userSessionData;
|
||||
|
||||
@RequestMapping(produces = {"application/json"}, method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
protected HashMap<Integer, HashMap> getUsers (HttpServletRequest req) {
|
||||
|
||||
try {
|
||||
Connection connection = DatabaseUtilities.getConnection(getWebSession());
|
||||
String query = "SELECT * FROM user_data";
|
||||
|
||||
try {
|
||||
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
|
||||
ResultSet.CONCUR_READ_ONLY);
|
||||
ResultSet results = statement.executeQuery(query);
|
||||
HashMap<Integer,HashMap> allUsersMap = new HashMap();
|
||||
|
||||
if ((results != null) && (results.first() == true)) {
|
||||
ResultSetMetaData resultsMetaData = results.getMetaData();
|
||||
StringBuffer output = new StringBuffer();
|
||||
|
||||
while (results.next()) {
|
||||
int id = results.getInt(0);
|
||||
HashMap<String,String> userMap = new HashMap<>();
|
||||
userMap.put("first", results.getString(1));
|
||||
userMap.put("last", results.getString(2));
|
||||
userMap.put("cc", results.getString(3));
|
||||
userMap.put("ccType", results.getString(4));
|
||||
userMap.put("cookie", results.getString(5));
|
||||
userMap.put("loginCOunt",Integer.toString(results.getInt(6)));
|
||||
allUsersMap.put(id,userMap);
|
||||
}
|
||||
userSessionData.setValue("allUsers",allUsersMap);
|
||||
return allUsersMap;
|
||||
|
||||
}
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
HashMap<String,String> errMap = new HashMap() {{
|
||||
put("err",sqle.getErrorCode() + "::" + sqle.getMessage());
|
||||
}};
|
||||
|
||||
return new HashMap<Integer,HashMap>() {{
|
||||
put(0,errMap);
|
||||
}};
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
HashMap<String,String> errMap = new HashMap() {{
|
||||
put("err",e.getMessage() + "::" + e.getCause());
|
||||
}};
|
||||
e.printStackTrace();
|
||||
return new HashMap<Integer,HashMap>() {{
|
||||
put(0,errMap);
|
||||
}};
|
||||
|
||||
|
||||
} finally {
|
||||
try {
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
HashMap<String,String> errMap = new HashMap() {{
|
||||
put("err",e.getMessage() + "::" + e.getCause());
|
||||
}};
|
||||
e.printStackTrace();
|
||||
return new HashMap<Integer,HashMap>() {{
|
||||
put(0,errMap);
|
||||
}};
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected WebSession getWebSession() {
|
||||
return webSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return "/access-control/list-users";
|
||||
}
|
||||
}
|
BIN
webgoat-lessons/missing-function-ac/src/main/resources/.DS_Store
vendored
Normal file
BIN
webgoat-lessons/missing-function-ac/src/main/resources/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
webgoat-lessons/missing-function-ac/src/main/resources/html/.DS_Store
vendored
Normal file
BIN
webgoat-lessons/missing-function-ac/src/main/resources/html/.DS_Store
vendored
Normal file
Binary file not shown.
@ -0,0 +1,82 @@
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:missing-function-ac-01-intro.adoc"></div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:missing-function-ac-02-client-controls.adoc"></div>
|
||||
|
||||
<script th:src="@{/lesson_js/missing-function-ac.js}" > </script>
|
||||
|
||||
<div class="attack-container">
|
||||
<div id="ac-menu-wrapper">
|
||||
<div id="ac-menu">
|
||||
<h3 class="menu-header">Account</h3>
|
||||
<div class="menu-section">
|
||||
<ul>
|
||||
<li>My Profile</li>
|
||||
<li>Privacy/Security</li>
|
||||
<li>Log Out</li>
|
||||
</ul>
|
||||
</div>
|
||||
<h3 class="menu-header">Messages</h3>
|
||||
<div class="menu-section">
|
||||
<ul>
|
||||
<li>Unread Messages (3)</li>
|
||||
<li>Compose Message</li>
|
||||
</ul>
|
||||
</div>
|
||||
<h3 class="hidden-menu-item menu-header">Admin</h3>
|
||||
<div class="menu-section hidden-menu-item">
|
||||
<ul>
|
||||
<li><a href="/users">Users</a></li>
|
||||
<li><a href="/config">Config</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<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" name="form"
|
||||
action="/WebGoat/access-control/hidden-menu">
|
||||
|
||||
<p>Hidden Item 1 <input name="hiddenMenu1" value="" type="TEXT" /></p>
|
||||
<p>Hidden Item 2 <input name="hiddenMenu2" value="" type="TEXT" /></p>
|
||||
<br/>
|
||||
<input name="submit" value="Submit" type="SUBMIT"/>
|
||||
|
||||
</form>
|
||||
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
|
||||
<div class="adoc-content" th:replace="doc:missing-function-ac-03-users.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"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/access-control/user-hash">
|
||||
|
||||
<p>Your Hash: <input name="userHash" value="" type="TEXT" /></p>
|
||||
<br/>
|
||||
<input name="submit" value="Submit" type="SUBMIT"/>
|
||||
|
||||
</form>
|
||||
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</html>
|
@ -0,0 +1,22 @@
|
||||
missing-function-access-control.title=Missing Function Level Access Control
|
||||
|
||||
access-control.hidden-menus.success=Correct! And not hard to find are they?!? One of these urls will be helpful in the next lab.
|
||||
access-control.hidden-menus.close=Close. Remember that when hacking ... details such as order,case and the like matter.
|
||||
access-control.hidden-menus.failure=Please try again.
|
||||
|
||||
access-control.hidden-menus.hint1=You can inspect the DOM or review the source in the proxy request/response cycle.
|
||||
access-control.hidden-menus.hint2=Look for indications of something that would not be available to a typical user
|
||||
access-control.hidden-menus.hint3=Look for something a super-user or administator might have available to them
|
||||
|
||||
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.hint1=If you haven't found the hidden menus from the earlier exercise, go do that now.
|
||||
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.hint3=Have you tried tampering the GET request? Can you find supported or unsupported methods? Can you trigger 500 errors?
|
||||
access-control.hash.hint4=There are actually two ways to solve this one. The first involves just changing a request header.
|
||||
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.hint6=If you're still looking for hints ... try changing the Content-type header in the GET request.
|
||||
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.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.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.
|
@ -0,0 +1,6 @@
|
||||
webgoat.customjs.accessControlMenu = function() {
|
||||
//webgoat.customjs.jquery('#ac-menu-ul').menu();
|
||||
webgoat.customjs.jquery('#ac-menu').accordion();
|
||||
}
|
||||
|
||||
webgoat.customjs.accessControlMenu();
|
@ -0,0 +1,9 @@
|
||||
== Missing Function Level Access Control
|
||||
|
||||
Access control, like output encoding XSS can be tricky to maintain and ensure it is enforced properly throughout an application, including at each method/function.
|
||||
|
||||
=== IDOR vs Missing Function Level Access Control
|
||||
|
||||
The fact is many people (including the author of this lesson) would lump function level access control and IDOR into 'Access Control'. For sake of OWASP, Top 10 and these lessons, we will make a
|
||||
distinction. The distinction most make is that IDOR is more of a 'horizontal' or 'lateral' access control issue, and missing function level access control 'exposes functionality'. Even though,
|
||||
the IDOR lesson here demonstrates how functionality may also be exposed, (at least to another user in the same role), we will look at other ways functionality might be exposed.
|
@ -0,0 +1,16 @@
|
||||
== Relying on Obscurity
|
||||
|
||||
If you are relying on HTML, CSS or javascript to hide links that users don't normally access.
|
||||
It's a little older, but there was a case of a network router trying to protect (hide) admin functions with javascript in the UI https://www.wired.com/2009/10/routers-still-vulnerable
|
||||
|
||||
=== Finding Hidden Items
|
||||
|
||||
There are usually hints to finding functionality the UI does not openly expose in ...
|
||||
|
||||
* HTML or javascript comments
|
||||
* Commented out elements
|
||||
* Items hidden via css controls/classes
|
||||
|
||||
=== Your Mission
|
||||
|
||||
Find two menu items not visible in menu below that are or would be of interest to an attacker/malicious user and put the labels for those menu items (there are no links right now in the menus).
|
@ -0,0 +1,10 @@
|
||||
== Just Try It
|
||||
|
||||
As the previous page noted, sometimes apps rely on client controls. to control access (obscurity). If you can find items that don't have visible links, just try them, see what happens. Yes, it
|
||||
can be that simple!
|
||||
|
||||
=== Gathering User Info
|
||||
|
||||
Often times, data dumps from vulnerabilities such as sql injection, but they can also come from poor or lacking access control.
|
||||
|
||||
It will likely take multiple steps and multiple attempts to get this one. Pay attention to the comments, leaked info. and you'll need to guess some. You may need to use another browser/account along the way. Start with the info. you already gathered (hidden menu items) to see if you can pull the list of users and then provide the 'Hash' for your own user account.
|
@ -0,0 +1,22 @@
|
||||
package org.owasp.webgoat.plugin;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.owasp.webgoat.users.WebGoatUser;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DisplayUserTest {
|
||||
|
||||
@Test
|
||||
public void TestDisplayUserCreation() {
|
||||
DisplayUser displayUser = new DisplayUser(new WebGoatUser("user1","password1"));
|
||||
assert(!displayUser.isAdmin());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TesDisplayUserHash() {
|
||||
DisplayUser displayUser = new DisplayUser(new WebGoatUser("user1","password1"));
|
||||
assert(displayUser.getUserHash().equals("cplTjehjI/e5ajqTxWaXhU5NW9UotJfXj+gcbPvfWWc="));
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package org.owasp.webgoat.plugin;
|
||||
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpointTest;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class MissingFunctionACHiddenMenusTest extends AssignmentEndpointTest {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MissingFunctionACHiddenMenus hiddenMenus = new MissingFunctionACHiddenMenus();
|
||||
init(hiddenMenus);
|
||||
this.mockMvc = standaloneSetup(hiddenMenus).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void HiddenMenusSuccess() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/hidden-menu")
|
||||
.param("hiddenMenu1", "Users")
|
||||
.param("hiddenMenu2", "Config"))
|
||||
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("access-control.hidden-menus.success"))))
|
||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void HiddenMenusClose() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/hidden-menu")
|
||||
.param("hiddenMenu1", "Config")
|
||||
.param("hiddenMenu2", "Users"))
|
||||
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("access-control.hidden-menus.close"))))
|
||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void HiddenMenusFailure() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/hidden-menu")
|
||||
.param("hiddenMenu1", "Foo")
|
||||
.param("hiddenMenu2", "Bar"))
|
||||
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("access-control.hidden-menus.failure"))))
|
||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package org.owasp.webgoat.plugin;
|
||||
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.owasp.webgoat.lessons.AbstractLesson;
|
||||
import org.owasp.webgoat.service.HintService;
|
||||
import org.owasp.webgoat.session.WebSession;
|
||||
import org.owasp.webgoat.users.UserService;
|
||||
import org.owasp.webgoat.users.WebGoatUser;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class MissingFunctionACUsersTest {
|
||||
private MockMvc mockMvc;
|
||||
@Mock
|
||||
private WebSession websession;
|
||||
@Mock
|
||||
private AbstractLesson lesson;
|
||||
@Mock
|
||||
private UserService userService;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MissingFunctionACUsers usersController = new MissingFunctionACUsers();
|
||||
this.mockMvc = standaloneSetup(usersController).build();
|
||||
ReflectionTestUtils.setField(usersController,"userService",userService);
|
||||
when(userService.getAllUsers()).thenReturn(getUsersList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestContentTypeApplicationJSON () throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/users")
|
||||
.header("Content-type","application/json"))
|
||||
.andDo(MockMvcResultHandlers.print())
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$[0].username", CoreMatchers.is("user1")))
|
||||
.andExpect(jsonPath("$[0].userHash",CoreMatchers.is("cplTjehjI/e5ajqTxWaXhU5NW9UotJfXj+gcbPvfWWc=")))
|
||||
.andExpect(jsonPath("$[1].admin",CoreMatchers.is(true)));
|
||||
|
||||
}
|
||||
|
||||
private List<WebGoatUser> getUsersList() {
|
||||
List <WebGoatUser> tempUsers = new ArrayList<>();
|
||||
tempUsers.add(new WebGoatUser("user1","password1"));
|
||||
tempUsers.add(new WebGoatUser("user2","password2","WEBGOAT_ADMIN"));
|
||||
tempUsers.add(new WebGoatUser("user3","password3", "WEBGOAT_USER"));
|
||||
return tempUsers;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package org.owasp.webgoat.plugin;
|
||||
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpointTest;
|
||||
import org.owasp.webgoat.users.UserService;
|
||||
import org.owasp.webgoat.users.WebGoatUser;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
|
||||
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class MissingFunctionYourHashTest extends AssignmentEndpointTest {
|
||||
private MockMvc mockMvc;
|
||||
private DisplayUser mockDisplayUser;
|
||||
|
||||
@Mock
|
||||
protected UserService userService;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MissingFunctionACYourHash yourHashTest = new MissingFunctionACYourHash();
|
||||
init(yourHashTest);
|
||||
this.mockMvc = standaloneSetup(yourHashTest).build();
|
||||
this.mockDisplayUser = new DisplayUser(new WebGoatUser("user","userPass"));
|
||||
ReflectionTestUtils.setField(yourHashTest,"userService",userService);
|
||||
when(mockDisplayUser.getUserHash()).thenReturn("2340928sadfajsdalsNfwrBla=");
|
||||
when(userService.loadUserByUsername(anyString())).thenReturn(new WebGoatUser("user","userPass"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void HashDoesNotMatch() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/user-hash")
|
||||
.param("userHash", "42"))
|
||||
.andExpect(status().isOk()).andDo(MockMvcResultHandlers.print())
|
||||
.andExpect(jsonPath("$.feedback", CoreMatchers.containsString("Keep trying, this one may take several attempts")))
|
||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashMatches() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/user-hash")
|
||||
.param("userHash", "2340928sadfajsdalsNfwrBla="))
|
||||
.andExpect(status().isOk()).andDo(MockMvcResultHandlers.print())
|
||||
.andExpect(jsonPath("$.feedback", CoreMatchers.containsString("Keep trying, this one may take several attempts")))
|
||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
||||
}
|
||||
|
||||
}
|
@ -27,7 +27,8 @@
|
||||
<module>xxe</module>
|
||||
<module>idor</module>
|
||||
<module>vulnerable-components</module>
|
||||
<module>auth-bypass</module>
|
||||
<module>auth-bypass</module>
|
||||
<module>missing-function-ac</module>
|
||||
<!-- uncomment below to include lesson template in build, also uncomment the dependency in webgoat-server/pom.xml to have it run in the project fully -->
|
||||
<!--<module>webgoat-lesson-template</module>-->
|
||||
</modules>
|
||||
|
@ -154,6 +154,11 @@
|
||||
<artifactId>auth-bypass</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>missing-function-ac</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--uncommment below to run/include lesson template in WebGoat Build-->
|
||||
<!--<dependency>-->
|
||||
|
Loading…
x
Reference in New Issue
Block a user