Rewrite lesson to be self-contained and not depend on the core of WebGoat for fetching users
Split the assignment into 2 assignments
This commit is contained in:
parent
9e6ed11aa7
commit
3ad51e6d6b
@ -34,15 +34,14 @@ public class WebGoatUser implements UserDetails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public WebGoatUser(String username, String password) {
|
public WebGoatUser(String username, String password) {
|
||||||
this.username = username;
|
this(username, password, ROLE_USER);
|
||||||
this.password = password;
|
|
||||||
createUser();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebGoatUser(String username, String password, String role) {
|
public WebGoatUser(String username, String password, String role) {
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.password = password;
|
this.password = password;
|
||||||
this.role = role;
|
this.role = role;
|
||||||
|
createUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,54 +1,87 @@
|
|||||||
package org.owasp.webgoat;
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
import lombok.Data;
|
import org.apache.http.HttpStatus;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class AccessControlTest extends IntegrationTest {
|
public class AccessControlTest extends IntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLesson() {
|
public void testLesson() {
|
||||||
startLesson("MissingFunctionAC");
|
startLesson("MissingFunctionAC");
|
||||||
|
assignment1();
|
||||||
|
assignment2();
|
||||||
|
assignment3();
|
||||||
|
|
||||||
Map<String, Object> params = new HashMap<>();
|
checkResults("/access-control");
|
||||||
params.clear();
|
}
|
||||||
params.put("hiddenMenu1", "Users");
|
|
||||||
params.put("hiddenMenu2", "Config");
|
|
||||||
|
|
||||||
|
private void assignment3() {
|
||||||
checkAssignment(url("/WebGoat/access-control/hidden-menu"), params, true);
|
//direct call should fail if user has not been created
|
||||||
String userHash =
|
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.contentType(ContentType.JSON)
|
.contentType(ContentType.JSON)
|
||||||
.get(url("/WebGoat/users"))
|
.get(url("/WebGoat/access-control/users-admin-fix"))
|
||||||
|
.then()
|
||||||
|
.statusCode(HttpStatus.SC_FORBIDDEN);
|
||||||
|
|
||||||
|
//create user
|
||||||
|
var userTemplate = """
|
||||||
|
{"username":"%s","password":"%s","admin": "true"}
|
||||||
|
""";
|
||||||
|
RestAssured.given()
|
||||||
|
.when()
|
||||||
|
.relaxedHTTPSValidation()
|
||||||
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
|
.contentType(ContentType.JSON)
|
||||||
|
.body(String.format(userTemplate, getWebgoatUser(), getWebgoatUser()))
|
||||||
|
.post(url("/WebGoat/access-control/users"))
|
||||||
|
.then()
|
||||||
|
.statusCode(HttpStatus.SC_OK);
|
||||||
|
|
||||||
|
//get the users
|
||||||
|
var userHash =
|
||||||
|
RestAssured.given()
|
||||||
|
.when()
|
||||||
|
.relaxedHTTPSValidation()
|
||||||
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
|
.contentType(ContentType.JSON)
|
||||||
|
.get(url("/WebGoat/access-control/users-admin-fix"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract()
|
.extract()
|
||||||
.jsonPath()
|
.jsonPath()
|
||||||
.get("find { it.username == \"" + getWebgoatUser() + "\" }.userHash");
|
.get("find { it.username == \"Jerry\" }.userHash");
|
||||||
|
|
||||||
params.clear();
|
checkAssignment(url("/WebGoat/access-control/user-hash-fix"), Map.of("userHash", userHash), true);
|
||||||
params.put("userHash", userHash);
|
|
||||||
checkAssignment(url("/WebGoat/access-control/user-hash"), params, true);
|
|
||||||
|
|
||||||
|
|
||||||
checkResults("/access-control");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
private void assignment2() {
|
||||||
public class Item {
|
var userHash =
|
||||||
private String username;
|
RestAssured.given()
|
||||||
private boolean admin;
|
.when()
|
||||||
private String userHash;
|
.relaxedHTTPSValidation()
|
||||||
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
|
.contentType(ContentType.JSON)
|
||||||
|
.get(url("/WebGoat/access-control/users"))
|
||||||
|
.then()
|
||||||
|
.statusCode(200)
|
||||||
|
.extract()
|
||||||
|
.jsonPath()
|
||||||
|
.get("find { it.username == \"Jerry\" }.userHash");
|
||||||
|
|
||||||
|
checkAssignment(url("/WebGoat/access-control/user-hash"), Map.of("userHash", userHash), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assignment1() {
|
||||||
|
var params = Map.of("hiddenMenu1", "Users", "hiddenMenu2", "Config");
|
||||||
|
checkAssignment(url("/WebGoat/access-control/hidden-menu"), params, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package org.owasp.webgoat.missing_ac;
|
package org.owasp.webgoat.missing_ac;
|
||||||
|
|
||||||
import org.owasp.webgoat.users.WebGoatUser;
|
import lombok.Getter;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
|
||||||
@ -31,55 +31,32 @@ import java.util.Base64;
|
|||||||
* projects.
|
* projects.
|
||||||
* <p>
|
* <p>
|
||||||
*/
|
*/
|
||||||
|
@Getter
|
||||||
public class DisplayUser {
|
public class DisplayUser {
|
||||||
//intended to provide a display version of WebGoatUser for admins to view user attributes
|
//intended to provide a display version of WebGoatUser for admins to view user attributes
|
||||||
|
|
||||||
|
|
||||||
private String username;
|
private String username;
|
||||||
private boolean admin;
|
private boolean admin;
|
||||||
private String userHash;
|
private String userHash;
|
||||||
|
|
||||||
public DisplayUser(WebGoatUser user) {
|
public DisplayUser(User user, String passwordSalt) {
|
||||||
this.username = user.getUsername();
|
this.username = user.getUsername();
|
||||||
this.admin = false;
|
this.admin = user.isAdmin();
|
||||||
|
|
||||||
for (GrantedAuthority authority : user.getAuthorities()) {
|
|
||||||
this.admin = (authority.getAuthority().contains("WEBGOAT_ADMIN")) ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create userHash on the fly
|
|
||||||
//TODO: persist userHash
|
|
||||||
try {
|
try {
|
||||||
this.userHash = genUserHash(user.getUsername(), user.getPassword());
|
this.userHash = genUserHash(user.getUsername(), user.getPassword(), passwordSalt);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
//TODO: implement better fallback operation
|
|
||||||
this.userHash = "Error generating user hash";
|
this.userHash = "Error generating user hash";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String genUserHash(String username, String password) throws Exception {
|
protected String genUserHash(String username, String password, String passwordSalt) throws Exception {
|
||||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||||
// salting is good, but static & too predictable ... short too for a salt
|
// salting is good, but static & too predictable ... short too for a salt
|
||||||
String salted = password + "DeliberatelyInsecure1234" + username;
|
String salted = password + passwordSalt + username;
|
||||||
//md.update(salted.getBytes("UTF-8")); // Change this to "UTF-16" if needed
|
//md.update(salted.getBytes("UTF-8")); // Change this to "UTF-16" if needed
|
||||||
byte[] hash = md.digest(salted.getBytes("UTF-8"));
|
byte[] hash = md.digest(salted.getBytes(StandardCharsets.UTF_8));
|
||||||
String encoded = Base64.getEncoder().encodeToString(hash);
|
return Base64.getEncoder().encodeToString(hash);
|
||||||
return encoded;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public String getUsername() {
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAdmin() {
|
|
||||||
return admin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUserHash() {
|
|
||||||
return userHash;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
package org.owasp.webgoat.missing_ac;
|
||||||
|
|
||||||
|
import org.owasp.webgoat.LessonDataSource;
|
||||||
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
|
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
||||||
|
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MissingAccessControlUserRepository {
|
||||||
|
|
||||||
|
private final NamedParameterJdbcTemplate jdbcTemplate;
|
||||||
|
private final RowMapper<User> mapper = (rs, rowNum) -> new User(rs.getString("username"), rs.getString("password"), rs.getBoolean("admin"));
|
||||||
|
|
||||||
|
public MissingAccessControlUserRepository(LessonDataSource lessonDataSource) {
|
||||||
|
this.jdbcTemplate = new NamedParameterJdbcTemplate(lessonDataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<User> findAllUsers() {
|
||||||
|
return jdbcTemplate.query("select username, password, admin from access_control_users", mapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User findByUsername(String username) {
|
||||||
|
var users = jdbcTemplate.query("select username, password, admin from access_control_users where username=:username",
|
||||||
|
new MapSqlParameterSource().addValue("username", username),
|
||||||
|
mapper);
|
||||||
|
if (CollectionUtils.isEmpty(users)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return users.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User save(User user) {
|
||||||
|
jdbcTemplate.update("INSERT INTO access_control_users(username, password, admin) VALUES(:username,:password,:admin)",
|
||||||
|
new MapSqlParameterSource()
|
||||||
|
.addValue("username", user.getUsername())
|
||||||
|
.addValue("password", user.getPassword())
|
||||||
|
.addValue("admin", user.isAdmin()));
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -29,6 +29,9 @@ import org.springframework.stereotype.Component;
|
|||||||
@Component
|
@Component
|
||||||
public class MissingFunctionAC extends Lesson {
|
public class MissingFunctionAC extends Lesson {
|
||||||
|
|
||||||
|
public static final String PASSWORD_SALT_SIMPLE = "DeliberatelyInsecure1234";
|
||||||
|
public static final String PASSWORD_SALT_ADMIN = "DeliberatelyInsecure1235";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Category getDefaultCategory() {
|
public Category getDefaultCategory() {
|
||||||
return Category.ACCESS_CONTROL;
|
return Category.ACCESS_CONTROL;
|
||||||
|
@ -25,8 +25,6 @@ package org.owasp.webgoat.missing_ac;
|
|||||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.assignments.AttackResult;
|
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.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
@ -37,10 +35,6 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@RestController
|
@RestController
|
||||||
@AssignmentHints({"access-control.hidden-menus.hint1","access-control.hidden-menus.hint2","access-control.hidden-menus.hint3"})
|
@AssignmentHints({"access-control.hidden-menus.hint1","access-control.hidden-menus.hint2","access-control.hidden-menus.hint3"})
|
||||||
public class MissingFunctionACHiddenMenus extends AssignmentEndpoint {
|
public class MissingFunctionACHiddenMenus extends AssignmentEndpoint {
|
||||||
//UserSessionData is bound to session and can be used to persist data across multiple assignments
|
|
||||||
@Autowired
|
|
||||||
UserSessionData userSessionData;
|
|
||||||
|
|
||||||
|
|
||||||
@PostMapping(path = "/access-control/hidden-menu", produces = {"application/json"})
|
@PostMapping(path = "/access-control/hidden-menu", produces = {"application/json"})
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
|
@ -22,75 +22,77 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.missing_ac;
|
package org.owasp.webgoat.missing_ac;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.owasp.webgoat.users.UserService;
|
import org.owasp.webgoat.session.WebSession;
|
||||||
import org.owasp.webgoat.users.WebGoatUser;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_ADMIN;
|
||||||
|
import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_SIMPLE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by jason on 1/5/17.
|
* Created by jason on 1/5/17.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
|
@AllArgsConstructor
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class MissingFunctionACUsers {
|
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
|
private final MissingAccessControlUserRepository userRepository;
|
||||||
@Autowired
|
private final WebSession webSession;
|
||||||
private UserService userService;
|
|
||||||
|
|
||||||
@RequestMapping(path = {"users"}, method = RequestMethod.GET)
|
@GetMapping(path = {"access-control/users"})
|
||||||
public ModelAndView listUsers(HttpServletRequest request) {
|
public ModelAndView listUsers() {
|
||||||
|
|
||||||
ModelAndView model = new ModelAndView();
|
ModelAndView model = new ModelAndView();
|
||||||
model.setViewName("list_users");
|
model.setViewName("list_users");
|
||||||
List<WebGoatUser> allUsers = userService.getAllUsers();
|
List<User> allUsers = userRepository.findAllUsers();
|
||||||
model.addObject("numUsers", allUsers.size());
|
model.addObject("numUsers", allUsers.size());
|
||||||
//add display user objects in place of direct users
|
//add display user objects in place of direct users
|
||||||
List<DisplayUser> displayUsers = new ArrayList<>();
|
List<DisplayUser> displayUsers = new ArrayList<>();
|
||||||
for (WebGoatUser user : allUsers) {
|
for (User user : allUsers) {
|
||||||
displayUsers.add(new DisplayUser(user));
|
displayUsers.add(new DisplayUser(user, PASSWORD_SALT_SIMPLE));
|
||||||
}
|
}
|
||||||
model.addObject("allUsers", displayUsers);
|
model.addObject("allUsers", displayUsers);
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(path = {"users", "/"}, method = RequestMethod.GET,consumes = "application/json")
|
@GetMapping(path = {"access-control/users"}, consumes = "application/json")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public List<DisplayUser> usersService(HttpServletRequest request) {
|
public ResponseEntity<List<DisplayUser>> usersService() {
|
||||||
|
return ResponseEntity.ok(userRepository.findAllUsers().stream().map(user -> new DisplayUser(user, PASSWORD_SALT_SIMPLE)).collect(Collectors.toList()));
|
||||||
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")
|
@GetMapping(path = {"access-control/users-admin-fix"}, consumes = "application/json")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
//@PreAuthorize()
|
public ResponseEntity<List<DisplayUser>> usersFixed() {
|
||||||
public WebGoatUser addUser(@RequestBody WebGoatUser newUser) {
|
var currentUser = userRepository.findByUsername(webSession.getUserName());
|
||||||
|
if (currentUser != null && currentUser.isAdmin()) {
|
||||||
|
return ResponseEntity.ok(userRepository.findAllUsers().stream().map(user -> new DisplayUser(user, PASSWORD_SALT_ADMIN)).collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(path = {"access-control/users", "access-control/users-admin-fix"}, consumes = "application/json", produces = "application/json")
|
||||||
|
@ResponseBody
|
||||||
|
public User addUser(@RequestBody User newUser) {
|
||||||
try {
|
try {
|
||||||
userService.addUser(newUser.getUsername(),newUser.getPassword());
|
userRepository.save(newUser);
|
||||||
return userService.loadUserByUsername(newUser.getUsername());
|
return newUser;
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
log.error("Error creating new User", ex);
|
log.error("Error creating new User", ex);
|
||||||
//TODO: implement error handling ...
|
|
||||||
} finally {
|
|
||||||
// no streams or other resources opened ... nothing to do, right?
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,3 +100,4 @@ public class MissingFunctionACUsers {
|
|||||||
//TODO implement delete method with id param and authorization
|
//TODO implement delete method with id param and authorization
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -22,35 +22,33 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.missing_ac;
|
package org.owasp.webgoat.missing_ac;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.assignments.AttackResult;
|
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.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_SIMPLE;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@AssignmentHints({"access-control.hash.hint1","access-control.hash.hint2","access-control.hash.hint3",
|
@AssignmentHints({"access-control.hash.hint1", "access-control.hash.hint2", "access-control.hash.hint3", "access-control.hash.hint4", "access-control.hash.hint5"})
|
||||||
"access-control.hash.hint4","access-control.hash.hint5","access-control.hash.hint6","access-control.hash.hint7",
|
@RequiredArgsConstructor
|
||||||
"access-control.hash.hint8","access-control.hash.hint9","access-control.hash.hint10","access-control.hash.hint11","access-control.hash.hint12"})
|
|
||||||
public class MissingFunctionACYourHash extends AssignmentEndpoint {
|
public class MissingFunctionACYourHash extends AssignmentEndpoint {
|
||||||
|
|
||||||
@Autowired
|
private final MissingAccessControlUserRepository userRepository;
|
||||||
private UserService userService;
|
|
||||||
|
|
||||||
@PostMapping(path = "/access-control/user-hash", produces = {"application/json"})
|
@PostMapping(path = "/access-control/user-hash", produces = {"application/json"})
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public AttackResult completed(String userHash) {
|
public AttackResult simple(String userHash) {
|
||||||
String currentUser = getWebSession().getUserName();
|
User user = userRepository.findByUsername("Jerry");
|
||||||
WebGoatUser user = userService.loadUserByUsername(currentUser);
|
DisplayUser displayUser = new DisplayUser(user, PASSWORD_SALT_SIMPLE);
|
||||||
DisplayUser displayUser = new DisplayUser(user);
|
|
||||||
if (userHash.equals(displayUser.getUserHash())) {
|
if (userHash.equals(displayUser.getUserHash())) {
|
||||||
return success(this).feedback("access-control.hash.success").build();
|
return success(this).feedback("access-control.hash.success").build();
|
||||||
} else {
|
} else {
|
||||||
return failed(this).feedback("access-control.hash.close").build();
|
return failed(this).build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
|
||||||
|
*
|
||||||
|
* Copyright (c) 2002 - 2019 Bruce Mayhew
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Getting Source ==============
|
||||||
|
*
|
||||||
|
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.owasp.webgoat.missing_ac;
|
||||||
|
|
||||||
|
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||||
|
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||||
|
import org.owasp.webgoat.assignments.AttackResult;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_ADMIN;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@AssignmentHints({"access-control.hash.hint6", "access-control.hash.hint7",
|
||||||
|
"access-control.hash.hint8", "access-control.hash.hint9", "access-control.hash.hint10", "access-control.hash.hint11", "access-control.hash.hint12"})
|
||||||
|
public class MissingFunctionACYourHashAdmin extends AssignmentEndpoint {
|
||||||
|
|
||||||
|
private final MissingAccessControlUserRepository userRepository;
|
||||||
|
|
||||||
|
public MissingFunctionACYourHashAdmin(MissingAccessControlUserRepository userRepository) {
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(path = "/access-control/user-hash-fix", produces = {"application/json"})
|
||||||
|
@ResponseBody
|
||||||
|
public AttackResult admin(String userHash) {
|
||||||
|
//current user should be in the DB
|
||||||
|
//if not admin then return 403
|
||||||
|
|
||||||
|
var user = userRepository.findByUsername("Jerry");
|
||||||
|
var displayUser = new DisplayUser(user, PASSWORD_SALT_ADMIN);
|
||||||
|
if (userHash.equals(displayUser.getUserHash())) {
|
||||||
|
return success(this).feedback("access-control.hash.success").build();
|
||||||
|
} else {
|
||||||
|
return failed(this).feedback("access-control.hash.close").build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package org.owasp.webgoat.missing_ac;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class User {
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
private boolean admin;
|
||||||
|
}
|
@ -1,117 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
|
|
||||||
*
|
|
||||||
* Copyright (c) 2002 - 2019 Bruce Mayhew
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* Getting Source ==============
|
|
||||||
*
|
|
||||||
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.owasp.webgoat.missing_ac;
|
|
||||||
|
|
||||||
import org.owasp.webgoat.LessonDataSource;
|
|
||||||
import org.owasp.webgoat.session.UserSessionData;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
|
||||||
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.sql.Statement;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
public class Users {
|
|
||||||
|
|
||||||
private UserSessionData userSessionData;
|
|
||||||
private LessonDataSource dataSource;
|
|
||||||
|
|
||||||
public Users(UserSessionData userSessionData, LessonDataSource dataSource) {
|
|
||||||
this.userSessionData = userSessionData;
|
|
||||||
this.dataSource = dataSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping(produces = {"application/json"})
|
|
||||||
@ResponseBody
|
|
||||||
protected HashMap<Integer, HashMap> getUsers() {
|
|
||||||
|
|
||||||
try (Connection connection = dataSource.getConnection()) {
|
|
||||||
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)) {
|
|
||||||
while (results.next()) {
|
|
||||||
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(results.getInt(0), 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<>() {{
|
|
||||||
put(0, errMap);
|
|
||||||
}};
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,4 @@
|
|||||||
|
.hidden-menu-item {
|
||||||
|
display:none;
|
||||||
|
visibility:hidden;
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
CREATE TABLE access_control_users(
|
||||||
|
username varchar(40),
|
||||||
|
password varchar(40),
|
||||||
|
admin boolean
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO access_control_users VALUES ('Tom', 'qwertyqwerty1234', false);
|
||||||
|
INSERT INTO access_control_users VALUES ('Jerry', 'doesnotreallymatter', true);
|
||||||
|
INSERT INTO access_control_users VALUES ('Sylvester', 'testtesttest', false);
|
@ -5,6 +5,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="lesson-page-wrapper">
|
<div class="lesson-page-wrapper">
|
||||||
|
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/ac.css}"/>
|
||||||
<div class="adoc-content" th:replace="doc:missing-function-ac-02-client-controls.adoc"></div>
|
<div class="adoc-content" th:replace="doc:missing-function-ac-02-client-controls.adoc"></div>
|
||||||
|
|
||||||
<div class="attack-container">
|
<div class="attack-container">
|
||||||
@ -17,7 +18,6 @@
|
|||||||
|
|
||||||
<div class="collapse navbar-collapse" id="alignment-example">
|
<div class="collapse navbar-collapse" id="alignment-example">
|
||||||
|
|
||||||
<!-- Links -->
|
|
||||||
<ul class="nav navbar-nav">
|
<ul class="nav navbar-nav">
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Account<span class="caret"></span></a>
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Account<span class="caret"></span></a>
|
||||||
@ -37,8 +37,9 @@
|
|||||||
<li class="hidden-menu-item dropdown">
|
<li class="hidden-menu-item dropdown">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Admin<span class="caret"></span></a>
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Admin<span class="caret"></span></a>
|
||||||
<ul class="dropdown-menu" aria-labelledby="admin">
|
<ul class="dropdown-menu" aria-labelledby="admin">
|
||||||
<li><a href="/users">Users</a></li>
|
<li><a href="/access-control/users">Users</a></li>
|
||||||
<li><a href="/config">Config</a></li>
|
<li><a href="/access-control/users-admin-fix">Users</a></li>
|
||||||
|
<li><a href="/access-control/config">Config</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -89,4 +90,26 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="lesson-page-wrapper">
|
||||||
|
|
||||||
|
<div class="adoc-content" th:replace="doc:missing-function-ac-04-users-fixed.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-fix">
|
||||||
|
|
||||||
|
<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>
|
</html>
|
||||||
|
@ -11,15 +11,15 @@ access-control.hidden-menus.hint3=Look for something a super-user or administato
|
|||||||
access-control.hash.success=Congrats! You really succeeded when you added the user.
|
access-control.hash.success=Congrats! You really succeeded when you added the user.
|
||||||
access-control.hash.close=Keep trying, this one may take several attempts & steps to achieve. See the hints for help.
|
access-control.hash.close=Keep trying, this one may take several attempts & steps to achieve. See the hints for help.
|
||||||
|
|
||||||
access-control.hash.hint1=There is an easier way and a 'harder' way to achieve this, the easier way involves one simple change in a GET request.
|
access-control.hash.hint1=This assignment involves one simple change in a GET request.
|
||||||
access-control.hash.hint2=If you haven't found the hidden menus from the earlier exercise, go do that first.
|
access-control.hash.hint2=If you haven't found the hidden menus from the earlier exercise, go do that first.
|
||||||
access-control.hash.hint3=When you look at the users page, there is a hint that more info is viewable by a given role.
|
access-control.hash.hint3=When you look at the users page, there is a hint that more info is viewable by a given role.
|
||||||
access-control.hash.hint4=For the easy way, have you tried tampering the GET request? Different content-types?
|
access-control.hash.hint4=Have you tried tampering the GET request? Different content-types?
|
||||||
access-control.hash.hint5=For the 'easy' way, modify the GET request to /users to include 'Content-Type: application/json'
|
access-control.hash.hint5=Modify the GET request to `/access-control/users` to include 'Content-Type: application/json'
|
||||||
access-control.hash.hint6=Now for the harder way ... it builds on the easier way
|
access-control.hash.hint6=Now for the harder way ... it builds on the easier way
|
||||||
access-control.hash.hint7=If the request to view users, were a 'service' or 'RESTful' endpoint, what would be different about it?
|
access-control.hash.hint7=If the request to view users, were a 'service' or 'RESTful' endpoint, what would be different about it?
|
||||||
access-control.hash.hint8=If you're still looking for hints ... try changing the Content-type header as in the GET request.
|
access-control.hash.hint8=If you're still looking for hints ... try changing the Content-type header as in the GET request.
|
||||||
access-control.hash.hint9=You also need to deliver a proper payload for the request (look at how registration works). This should be formatted in line with the content-type you just defined.
|
access-control.hash.hint9=You also need to deliver a proper payload for the request (look at how registration works). This should be formatted in line with the content-type you just defined.
|
||||||
access-control.hash.hint10=You will want to add WEBGOAT_ADMIN for the user's role. Yes, you'd have to guess/fuzz this in a real-world setting.
|
access-control.hash.hint10=You will want to add your own username with an admin role. Yes, you'd have to guess/fuzz this in a real-world setting.
|
||||||
access-control.hash.hint11=OK, here it is. First, create an admin user ... Change the method to POST, change the content-type to "application/json". And your payload should look something like: {"username":"newUser2","password":"newUser12","matchingPassword":"newUser12","role":"WEBGOAT_ADMIN"}
|
access-control.hash.hint11=OK, here it is. First, create an admin user ... Change the method to POST, change the content-type to "application/json". And your payload should look something like: {"username":"newUser2","password":"newUser12","admin": "true"}
|
||||||
access-control.hash.hint12=Now log in as that user and bring up WebGoat/users. Copy your hash and log back in to your original account and input it there to get credit.
|
access-control.hash.hint12=Now log in as that user and bring up WebGoat/users. Copy your hash and log back in to your original account and input it there to get credit.
|
||||||
|
@ -12,4 +12,4 @@ It will likely take multiple steps and multiple attempts to get this one:
|
|||||||
- You'll need to do some guessing too.
|
- You'll need to do some guessing too.
|
||||||
- You may need to use another browser/account along the way.
|
- You may need to use another browser/account along the way.
|
||||||
|
|
||||||
Start with the information you already gathered (hidden menu items) to see if you can pull the list of users and then provide the 'Hash' for your user account.
|
Start with the information you already gathered (hidden menu items) to see if you can pull the list of users and then provide the 'hash' for Jerry's account.
|
@ -0,0 +1,5 @@
|
|||||||
|
== The company fixed the problem, right?
|
||||||
|
|
||||||
|
The company found out the endpoint was a bit too open, they made an emergency fixed and not only admin users can list all users.
|
||||||
|
|
||||||
|
Start with the information you already gathered (hidden menu items) to see if you can pull the list of users and then provide the 'hash' for Jerry's account.
|
@ -22,23 +22,22 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.missing_ac;
|
package org.owasp.webgoat.missing_ac;
|
||||||
|
|
||||||
|
import org.assertj.core.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
|
||||||
import org.owasp.webgoat.users.WebGoatUser;
|
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_SIMPLE;
|
||||||
public class DisplayUserTest {
|
|
||||||
|
class DisplayUserTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void TestDisplayUserCreation() {
|
void testDisplayUserCreation() {
|
||||||
DisplayUser displayUser = new DisplayUser(new WebGoatUser("user1","password1"));
|
DisplayUser displayUser = new DisplayUser(new User("user1", "password1", true), PASSWORD_SALT_SIMPLE);
|
||||||
assert(!displayUser.isAdmin());
|
Assertions.assertThat(displayUser.isAdmin()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void TesDisplayUserHash() {
|
void testDisplayUserHash() {
|
||||||
DisplayUser displayUser = new DisplayUser(new WebGoatUser("user1","password1"));
|
DisplayUser displayUser = new DisplayUser(new User("user1", "password1", false), PASSWORD_SALT_SIMPLE);
|
||||||
assert(displayUser.getUserHash().equals("cplTjehjI/e5ajqTxWaXhU5NW9UotJfXj+gcbPvfWWc="));
|
Assertions.assertThat(displayUser.getUserHash()).isEqualTo("cplTjehjI/e5ajqTxWaXhU5NW9UotJfXj+gcbPvfWWc=");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,53 +28,53 @@ import org.junit.jupiter.api.Test;
|
|||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import org.owasp.webgoat.users.UserService;
|
import org.owasp.webgoat.plugins.LessonTest;
|
||||||
import org.owasp.webgoat.users.WebGoatUser;
|
import org.owasp.webgoat.session.WebSession;
|
||||||
import org.springframework.test.util.ReflectionTestUtils;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
import java.util.List;
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
import static org.mockito.Mockito.when;
|
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.jsonPath;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
|
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
class MissingFunctionACUsersTest extends LessonTest {
|
||||||
public class MissingFunctionACUsersTest {
|
|
||||||
private MockMvc mockMvc;
|
@Autowired
|
||||||
@Mock
|
private MissingFunctionAC ac;
|
||||||
private UserService userService;
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setup() {
|
void setup() {
|
||||||
MissingFunctionACUsers usersController = new MissingFunctionACUsers();
|
when(webSession.getCurrentLesson()).thenReturn(ac);
|
||||||
this.mockMvc = standaloneSetup(usersController).build();
|
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
||||||
ReflectionTestUtils.setField(usersController,"userService",userService);
|
|
||||||
when(userService.getAllUsers()).thenReturn(getUsersList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void TestContentTypeApplicationJSON () throws Exception {
|
void getUsers() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/users")
|
mockMvc.perform(MockMvcRequestBuilders.get("/access-control/users")
|
||||||
.header("Content-type", "application/json"))
|
.header("Content-type", "application/json"))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$[0].username", CoreMatchers.is("user1")))
|
.andExpect(jsonPath("$[0].username", CoreMatchers.is("Tom")))
|
||||||
.andExpect(jsonPath("$[0].userHash",CoreMatchers.is("cplTjehjI/e5ajqTxWaXhU5NW9UotJfXj+gcbPvfWWc=")))
|
.andExpect(jsonPath("$[0].userHash", CoreMatchers.is("Mydnhcy00j2b0m6SjmPz6PUxF9WIeO7tzm665GiZWCo=")))
|
||||||
.andExpect(jsonPath("$[1].admin",CoreMatchers.is(true)));
|
.andExpect(jsonPath("$[0].admin", CoreMatchers.is(false)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<WebGoatUser> getUsersList() {
|
@Test
|
||||||
List <WebGoatUser> tempUsers = new ArrayList<>();
|
void addUser() throws Exception {
|
||||||
tempUsers.add(new WebGoatUser("user1","password1"));
|
var user = """
|
||||||
tempUsers.add(new WebGoatUser("user2","password2","WEBGOAT_ADMIN"));
|
{"username":"newUser","password":"newUser12","admin": "true"}
|
||||||
tempUsers.add(new WebGoatUser("user3","password3", "WEBGOAT_USER"));
|
""";
|
||||||
return tempUsers;
|
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/users")
|
||||||
|
.header("Content-type", "application/json").content(user))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
|
mockMvc.perform(MockMvcRequestBuilders.get("/access-control/users")
|
||||||
|
.header("Content-type", "application/json"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.size()", is(4)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
package org.owasp.webgoat.missing_ac;
|
||||||
|
|
||||||
|
import org.hamcrest.CoreMatchers;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.owasp.webgoat.plugins.LessonTest;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_ADMIN;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
class MissingFunctionACYourHashAdminTest extends LessonTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MissingFunctionAC ac;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setup() {
|
||||||
|
when(webSession.getCurrentLesson()).thenReturn(ac);
|
||||||
|
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void solve() throws Exception {
|
||||||
|
var userHash = new DisplayUser(new User("Jerry", "doesnotreallymatter", true), PASSWORD_SALT_ADMIN).getUserHash();
|
||||||
|
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/user-hash-fix")
|
||||||
|
.param("userHash", userHash))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.feedback", CoreMatchers.containsString("Congrats! You really succeeded when you added the user.")))
|
||||||
|
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void wrongUserHash() throws Exception {
|
||||||
|
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/user-hash-fix")
|
||||||
|
.param("userHash", "wrong"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
||||||
|
}
|
||||||
|
}
|
@ -25,56 +25,40 @@ package org.owasp.webgoat.missing_ac;
|
|||||||
import org.hamcrest.CoreMatchers;
|
import org.hamcrest.CoreMatchers;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.owasp.webgoat.plugins.LessonTest;
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
|
||||||
import org.owasp.webgoat.assignments.AssignmentEndpointTest;
|
|
||||||
import org.owasp.webgoat.users.UserService;
|
|
||||||
import org.owasp.webgoat.users.WebGoatUser;
|
import org.owasp.webgoat.users.WebGoatUser;
|
||||||
import org.springframework.test.util.ReflectionTestUtils;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_SIMPLE;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
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.result.MockMvcResultMatchers.status;
|
||||||
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
|
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
class MissingFunctionYourHashTest extends LessonTest {
|
||||||
public class MissingFunctionYourHashTest extends AssignmentEndpointTest {
|
@Autowired
|
||||||
private MockMvc mockMvc;
|
private MissingFunctionAC ac;
|
||||||
private DisplayUser mockDisplayUser;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
protected UserService userService;
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setUp() {
|
public void setup() {
|
||||||
MissingFunctionACYourHash yourHashTest = new MissingFunctionACYourHash();
|
when(webSession.getCurrentLesson()).thenReturn(ac);
|
||||||
init(yourHashTest);
|
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
||||||
this.mockMvc = standaloneSetup(yourHashTest).build();
|
|
||||||
this.mockDisplayUser = new DisplayUser(new WebGoatUser("user", "userPass"));
|
|
||||||
ReflectionTestUtils.setField(yourHashTest, "userService", userService);
|
|
||||||
when(userService.loadUserByUsername(any())).thenReturn(new WebGoatUser("user", "userPass"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void HashDoesNotMatch() throws Exception {
|
void hashDoesNotMatch() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/user-hash")
|
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/user-hash")
|
||||||
.param("userHash", "42"))
|
.param("userHash", "42"))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.feedback", CoreMatchers.containsString("Keep trying, this one may take several attempts")))
|
|
||||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void hashMatches() throws Exception {
|
void hashMatches() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/user-hash")
|
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/user-hash")
|
||||||
.param("userHash", "2340928sadfajsdalsNfwrBla="))
|
.param("userHash", "SVtOlaa+ER+w2eoIIVE5/77umvhcsh5V8UyDLUa1Itg="))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.feedback", CoreMatchers.containsString("Keep trying, this one may take several attempts")))
|
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true)));
|
||||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user