#304 update to IDOR. Still experiencing 400 on EditOwnProfile endpoint

This commit is contained in:
Jason White 2017-01-06 13:04:03 -05:00
parent c2988eb771
commit fe4f568fc0
11 changed files with 94 additions and 30 deletions

View File

@ -25,11 +25,11 @@ public class IDORDiffAttributes extends AssignmentEndpoint {
if (diffAttribs.length < 2) {
return AttackResult.failed("You did not list two attributes string delimited");
}
if (diffAttribs[0].toLowerCase().equals("userid") && diffAttribs[1].toLowerCase().equals("admin") ||
diffAttribs[1].toLowerCase().equals("userid") && diffAttribs[0].toLowerCase().equals("admin")) {
return AttackResult.success("Correct, the two attributes not displayed are userId & admin. Keep those in mind");
if (diffAttribs[0].toLowerCase().trim().equals("userid") && diffAttribs[1].toLowerCase().trim().equals("role") ||
diffAttribs[1].toLowerCase().trim().equals("userid") && diffAttribs[0].toLowerCase().trim().equals("role")) {
return trackProgress(AttackResult.success("Correct, the two attributes not displayed are userId & role. Keep those in mind"));
} else {
return AttackResult.failed("Try again. Look in your browser dev tools or Proxy and compare to what's displayed on the screen.");
return trackProgress(AttackResult.failed("Try again. Look in your browser dev tools or Proxy and compare to what's displayed on the screen."));
}
}
}

View File

@ -3,6 +3,7 @@ package org.owasp.webgoat.plugin;
import org.owasp.webgoat.endpoints.AssignmentEndpoint;
import org.owasp.webgoat.lessons.AttackResult;
import org.owasp.webgoat.session.UserSessionData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
@ -41,18 +42,34 @@ import java.util.Map;
* @since January 3, 2017
*/
@Path("/IDOR/{userId}/profile-edit")
@Path("IDOR/profile/{userId}")
public class IDOREditOwnProfiile extends AssignmentEndpoint {
private Map<String,Map<String,String>> idorUserInfo = new HashMap<>();
@Autowired UserSessionData userSessionData;
@RequestMapping(method = RequestMethod.POST)
@RequestMapping(method = RequestMethod.PUT, consumes = "application/json")
public @ResponseBody
AttackResult completed(@PathVariable String userId, @RequestParam String password, HttpServletRequest request) throws IOException {
AttackResult completed(@PathVariable("userId") String userId, @RequestParam UserProfile userSubmittedProfile, HttpServletRequest request) {
UserSessionData userSessionData = getUserSessionData();
String authUserId = (String)userSessionData.getValue("idor-authenticated-user-id");
UserProfile currentUserProfile = new UserProfile(authUserId);
if (userSubmittedProfile.getUserId() != null && !userSubmittedProfile.getUserId().equals(authUserId)) {
return AttackResult.failed("Don't worry, we'll get to modifying someone else's profile, just modify your own for now.");
} else if (userSubmittedProfile.getUserId().equals(authUserId)) {
// this is commonly how vulnerable code will act ... updating w/out an authorization check
currentUserProfile.setColor(userSubmittedProfile.getColor());
currentUserProfile.setRole(userSubmittedProfile.getRole());
// we will persist in the session object for now
userSessionData.setValue("idor-updated-own-profile",currentUserProfile);
return trackProgress(AttackResult.failed("still working on this")); //TODO: How do we localize messages like this?
}
if (currentUserProfile.getColor().equals("black") && currentUserProfile.getRole() <= 1 ) {
return trackProgress(AttackResult.success("Good work! View the updated profile below",userSessionData.getValue("idor-updated-own-profile").toString()));
} else {
return trackProgress(AttackResult.failed("Please try again. Use the hints if need be."));
}
}

View File

@ -73,9 +73,7 @@ public class IDORLogin extends AssignmentEndpoint {
initIDORInfo();
UserSessionData userSessionData = getUserSessionData();
System.out.println("----:" + username + ":----");
if (idorUserInfo.containsKey(username)) {
System.out.println("****:" + username);
if ("tom".equals(username) && idorUserInfo.get("tom").get("password").equals(password)) {
userSessionData.setValue("idor-authenticated-as", username);
userSessionData.setValue("idor-authenticated-user-id", idorUserInfo.get(username).get("id"));

View File

@ -34,17 +34,15 @@ public class IDORViewOwnProfile extends Endpoint{
try {
if (userSessionData.getValue("idor-authenticated-as").equals("tom")) {
//going to use session auth to view this one
String authUserId = userSessionData.getValue("idor-authenticated-user-id");
String authUserId = (String)userSessionData.getValue("idor-authenticated-user-id");
UserProfile userProfile = new UserProfile(authUserId);
details.put("userId",userProfile.getUserId());
details.put("name",userProfile.getName());
details.put("color",userProfile.getColor());
details.put("size",userProfile.getSize());
details.put("admin",userProfile.isAdmin());
details.put("role",userProfile.getRole());
} else {
details.put("error","You do not have privileges to view the profile.");
System.out.println("Not auth'd as tom");
details.put("error","You do not have privileges to view the profile. Authenticate as tom first please.");
}
}catch (Exception ex) {
System.out.println(ex.getMessage());

View File

@ -32,27 +32,23 @@ public class IDORViewOwnProfileAltUrl extends AssignmentEndpoint{
try {
if (userSessionData.getValue("idor-authenticated-as").equals("tom")) {
//going to use session auth to view this one
String authUserId = userSessionData.getValue("idor-authenticated-user-id");
String authUserId = (String)userSessionData.getValue("idor-authenticated-user-id");
//don't care about http://localhost:8080 ... just want WebGoat/
String[] urlParts = url.split("/");
System.out.println("************");
System.out.println(urlParts[0]);
System.out.println(urlParts[1]);
System.out.println(urlParts[2]);
System.out.println(urlParts[3]);
System.out.println("************");
if (urlParts[0].equals("WebGoat") && urlParts[1].equals("IDOR") && urlParts[2].equals("profile") && urlParts[3].equals(authUserId)) {
UserProfile userProfile = new UserProfile(authUserId);
details.put("userId", userProfile.getUserId());
details.put("name", userProfile.getName());
details.put("color", userProfile.getColor());
details.put("size", userProfile.getSize());
details.put("admin", userProfile.isAdmin());
return AttackResult.success("congratultions, you have used the alternate Url/route to view your own profile.",details.toString());
details.put("role", userProfile.getRole());
return trackProgress(AttackResult.success("congratultions, you have used the alternate Url/route to view your own profile.",details.toString()));
} else {
return trackProgress(AttackResult.failed("please try again. The alternoute route is very similar to the previous way you viewed your profile. Only one difference really"));
}
} else {
return AttackResult.failed("please try again. The alternoute route is very similar to the previous way you viewed your profile. Only one difference really");
return trackProgress(AttackResult.failed("You need to authenticate as tom first."));
}
} catch (Exception ex) {
System.out.println(ex.getMessage());

View File

@ -9,6 +9,7 @@ public class UserProfile {
private String color;
private String size;
private boolean isAdmin;
private int role;
// anyting else?
public UserProfile() {}
@ -26,11 +27,13 @@ public class UserProfile {
this.name = "Tom Cat";
this.size = "small";
this.isAdmin = false;
this.role = 3;
} else if (id.equals("2342388")) {
this.color = "brown";
this.name = "Buffalo Bill";
this.size = "large";
this.isAdmin = false;
this.role = 3;
}
}
@ -40,6 +43,7 @@ public class UserProfile {
return "userId" + this.userId + htmlBreak +
"name" + this.name + htmlBreak +
"size" + this.size + htmlBreak +
"role" + this.role + htmlBreak +
"isAdmin" + this.isAdmin;
}
@ -83,4 +87,13 @@ public class UserProfile {
public void setAdmin(boolean admin) {
isAdmin = admin;
}
public int getRole() {
return role;
}
public void setRole(int role) {
this.role = role;
}
}

View File

@ -48,7 +48,7 @@ public class ViewOtherUserProfileEndpoint extends AssignmentEndpoint {
if (userSessionData.getValue("idor-authenticated-as").equals("bill") || userSessionData.getValue("idor-authenticated-as").equals("tom")) {
System.out.println("**** authenticated as " + userSessionData.getValue("idor-authenticated-as"));
//logged in
String authUserId = userSessionData.getValue("idor-authenticated-user-id");
String authUserId = (String)userSessionData.getValue("idor-authenticated-user-id");
//secure code would check to make sure authUserId matches userId ... and in this endpoint, we won't bother with that
UserProfile userProfile = new UserProfile(userId);
return trackProgress(AttackResult.failed("still working"));

View File

@ -127,6 +127,36 @@
</div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:IDOR_eidtOwn.adoc"></div>
<div class="attack-container">
<!-- using attack-form class on your form, will allow your request to be ajaxified and stay within the display framework for webgoat -->
<div>
<!-- using attack-form class on your form will allow your request to be ajaxified and stay within the display framework for webgoat -->
<!-- you can write your own custom forms, but standard form submission will take you to your endpoint and outside of the WebGoat framework -->
<!-- of course, you can write your own ajax submission /handling in your own javascript if you like -->
<!-- modify the action to point to the intended endpoint -->
<form class="attack-form" accept-charset="UNKNOWN"
method="GET" name="form"
action="/WebGoat/IDOR/profile"
enctype="application/json;charset=UTF-8">
<script th:src="@{/plugin_lessons/plugin/IDOR/js/idor.js}" />
<input name="View Profile" value="View Profile" type="button" onclick="onViewProfile();" />
</form>
</div>
<!-- do not remove the two following div's, this is where your feedback/output will land -->
<div class="attack-feedback"></div>
<div class="attack-output"></div>
<!-- ... of course, you can move them if you want to, but that will not look consistent to other lessons -->
</div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,

View File

@ -0,0 +1,4 @@
==== Edit Your Own Profile
If an application only exposes a way to view your profile or some object (i.e. 'GET' in RESTful), that does not mean you cannot edit it.
Use your intercept proxy to modify the request such that it would modify your profile. Change the color from 'yellow' to 'black' (sans single quotes).

View File

@ -0,0 +1,7 @@
=== Authenticate First, Abuse Authorization Later
Many access control issues are succeptible to attack from an authenticated-but-unauthorized user. So, let's start by legitimately authenticating. Then, we will look for ways to bypass or abuse Authorization.
The id and password for the account in this case are 'tom' and 'cat' (It is an insecure app, right?).
After authenticating, proceed to the next screen.

View File

@ -1,6 +1,7 @@
=== Guessing & Predicting Patterns
==== View Your Own Profile Another Way
The application we are working with seems to follow a RESTful pattern so far as the profile goes. Many apps have roles in which an elevated user may access content of another.
In that case, just /profile won't work since the own user's session/authentication data won't tell us whose profile they want view.
So, what do you think is a likely pattern to view your own profile using a direct object reference?
So, what do you think is a likely pattern to view your own profile explicitly using a direct object reference?