Merge pull request #378 from misfir3/missing-function-level-ac

Missing function level ac
This commit is contained in:
misfir3 2017-08-09 00:10:22 -06:00 committed by GitHub
commit 51c9363162
42 changed files with 1091 additions and 26 deletions

View File

@ -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");
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;}

View 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;
}

View 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>

View File

@ -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>

View File

@ -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)) {

View File

@ -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.

View File

@ -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

Binary file not shown.

View 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>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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;
}
}

View File

@ -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";
}
}

View File

@ -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());
}
}

View File

@ -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
}

View File

@ -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());
}
}
}

View File

@ -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";
}
}

Binary file not shown.

View File

@ -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>

View File

@ -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.

View File

@ -0,0 +1,6 @@
webgoat.customjs.accessControlMenu = function() {
//webgoat.customjs.jquery('#ac-menu-ul').menu();
webgoat.customjs.jquery('#ac-menu').accordion();
}
webgoat.customjs.accessControlMenu();

View File

@ -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.

View File

@ -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).

View File

@ -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.

View File

@ -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="));
}
}

View File

@ -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)));
}
}

View File

@ -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;
}
}

View File

@ -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)));
}
}

View File

@ -28,6 +28,7 @@
<module>idor</module>
<module>vulnerable-components</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>

View File

@ -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>-->