#304 incremental addition for IDOR, still experiencing 400 with PUT method

This commit is contained in:
Jason White 2017-01-09 14:02:00 -05:00
parent fe4f568fc0
commit 4e9b30d7f6
11 changed files with 259 additions and 28 deletions

View File

@ -12,8 +12,35 @@ import javax.ws.rs.Path;
import java.io.IOException; import java.io.IOException;
/** /**
* Created by jason on 1/5/17. * ************************************************************************************************
* 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>
*
* @author misfir3
* @version $Id: $Id
* @since January 3, 2017
*/ */
@Path("IDOR/diff-attributes") @Path("IDOR/diff-attributes")
public class IDORDiffAttributes extends AssignmentEndpoint { public class IDORDiffAttributes extends AssignmentEndpoint {
@ -23,7 +50,7 @@ public class IDORDiffAttributes extends AssignmentEndpoint {
attributes = attributes.trim(); attributes = attributes.trim();
String[] diffAttribs = attributes.split(","); String[] diffAttribs = attributes.split(",");
if (diffAttribs.length < 2) { if (diffAttribs.length < 2) {
return AttackResult.failed("You did not list two attributes string delimited"); return AttackResult.failed("You did not list two attributes, comma delimited");
} }
if (diffAttribs[0].toLowerCase().trim().equals("userid") && diffAttribs[1].toLowerCase().trim().equals("role") || 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")) { diffAttribs[1].toLowerCase().trim().equals("userid") && diffAttribs[0].toLowerCase().trim().equals("role")) {

View File

@ -43,26 +43,42 @@ import java.util.Map;
*/ */
@Path("IDOR/profile/{userId}") @Path("IDOR/profile/{userId}")
public class IDOREditOwnProfiile extends AssignmentEndpoint { public class IDOREditOtherProfiile extends AssignmentEndpoint {
@Autowired UserSessionData userSessionData; @Autowired UserSessionData userSessionData;
@RequestMapping(method = RequestMethod.PUT, consumes = "application/json") @RequestMapping(method = RequestMethod.PUT, consumes = "application/json")
public @ResponseBody public @ResponseBody
AttackResult completed(@PathVariable("userId") String userId, @RequestParam UserProfile userSubmittedProfile, HttpServletRequest request) { AttackResult completed(@PathVariable("userId") String userId, @RequestParam UserProfile userSubmittedProfile) {
String authUserId = (String)userSessionData.getValue("idor-authenticated-user-id"); String authUserId = (String)userSessionData.getValue("idor-authenticated-user-id");
UserProfile currentUserProfile = new UserProfile(authUserId); // this is where it starts ... accepting the user submitted ID and assuming it will be the same as the logged in userId and not checking for proper authorization
// Certain roles can sometimes edit others' profiles, but we shouldn't just assume that and let everyone, right?
// Except that this is a vulnerable app ... so we will
UserProfile currentUserProfile = new UserProfile(userId);
if (userSubmittedProfile.getUserId() != null && !userSubmittedProfile.getUserId().equals(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."); // let's get this started ...
} 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.setColor(userSubmittedProfile.getColor());
currentUserProfile.setRole(userSubmittedProfile.getRole()); currentUserProfile.setRole(userSubmittedProfile.getRole());
// we will persist in the session object for now // we will persist in the session object for now in case we want to refer back or use it later
userSessionData.setValue("idor-updated-own-profile",currentUserProfile); userSessionData.setValue("idor-updated-other-profile",currentUserProfile);
if (currentUserProfile.getRole() <= 1 && currentUserProfile.getColor().toLowerCase().equals("red")) {
return trackProgress(AttackResult.success("Well done, you have modified someone else's profile (as displayed below)",currentUserProfile.profileToMap().toString()));
}
if (currentUserProfile.getRole() > 1 && currentUserProfile.getColor().toLowerCase().equals("red")) {
return trackProgress(AttackResult.success("Close ... you've got the technique. Now try for a lower role number)",currentUserProfile.profileToMap().toString()));
}
if (currentUserProfile.getRole() <= 1 && !currentUserProfile.getColor().toLowerCase().equals("red")) {
return trackProgress(AttackResult.success("Close ... you've got the technique. Now change the color in their profile to red.)",currentUserProfile.profileToMap().toString()));
}
// else
return trackProgress(AttackResult.success("Try again. Use the hints if you need to.",currentUserProfile.profileToMap().toString()));
} else if (userSubmittedProfile.getUserId().equals(authUserId)) {
return AttackResult.failed("Modifying your own profile is good, but we want to do this to Buffalo Bill's profile.");
} }
if (currentUserProfile.getColor().equals("black") && currentUserProfile.getRole() <= 1 ) { if (currentUserProfile.getColor().equals("black") && currentUserProfile.getRole() <= 1 ) {

View File

@ -0,0 +1,82 @@
package org.owasp.webgoat.plugin;
import org.owasp.webgoat.endpoints.AssignmentEndpoint;
import org.owasp.webgoat.endpoints.Endpoint;
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.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Path;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* ************************************************************************************************
* 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>
*
* @author misfir3
* @version $Id: $Id
* @since January 3, 2017
*/
@Path("IDOR/profile/{userId}")
public class IDORViewOtherProfile extends AssignmentEndpoint{
@Autowired
UserSessionData userSessionData;
@RequestMapping(produces = {"application/json"}, method = RequestMethod.GET)
@ResponseBody
public AttackResult completed(@PathVariable("userId") String userId, HttpServletResponse resp) {
Map<String,Object> details = new HashMap<>();
if (userSessionData.getValue("idor-authenticated-as").equals("tom")) {
//going to use session auth to view this one
String authUserId = (String)userSessionData.getValue("idor-authenticated-user-id");
if(userId != null && !userId.equals(authUserId)) {
//on the right track
UserProfile requestedProfile = new UserProfile(userId);
// secure code would ensure there was a horizontal access control check prior to dishing up the requested profile
if (requestedProfile.getUserId().equals("2342388")){
return trackProgress(AttackResult.success("Well done, you found someone else's profile",requestedProfile.profileToMap().toString()));
} else {
return trackProgress((AttackResult.failed("You're on the right path, try a different id")));
}
} else {
return trackProgress((AttackResult.failed("Try again. You need to use the same method/URL you used to access your own profile via direct object reference.")));
}
}
return trackProgress((AttackResult.failed("Try again. ")));
}
}

View File

@ -20,8 +20,35 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**
* Created by jason on 1/5/17. * ************************************************************************************************
* 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>
*
* @author misfir3
* @version $Id: $Id
* @since January 3, 2017
*/ */
public class IDORViewOwnProfile extends Endpoint{ public class IDORViewOwnProfile extends Endpoint{
@Autowired @Autowired

View File

@ -17,8 +17,35 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* Created by jason on 1/5/17. * ************************************************************************************************
* 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>
*
* @author misfir3
* @version $Id: $Id
* @since January 3, 2017
*/ */
@Path("IDOR/profile/alt-path") @Path("IDOR/profile/alt-path")
public class IDORViewOwnProfileAltUrl extends AssignmentEndpoint{ public class IDORViewOwnProfileAltUrl extends AssignmentEndpoint{
@ -37,12 +64,7 @@ public class IDORViewOwnProfileAltUrl extends AssignmentEndpoint{
String[] urlParts = url.split("/"); String[] urlParts = url.split("/");
if (urlParts[0].equals("WebGoat") && urlParts[1].equals("IDOR") && urlParts[2].equals("profile") && urlParts[3].equals(authUserId)) { if (urlParts[0].equals("WebGoat") && urlParts[1].equals("IDOR") && urlParts[2].equals("profile") && urlParts[3].equals(authUserId)) {
UserProfile userProfile = new UserProfile(authUserId); UserProfile userProfile = new UserProfile(authUserId);
details.put("userId", userProfile.getUserId()); return trackProgress(AttackResult.success("congratultions, you have used the alternate Url/route to view your own profile.",userProfile.profileToMap().toString()));
details.put("name", userProfile.getName());
details.put("color", userProfile.getColor());
details.put("size", userProfile.getSize());
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 { } 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")); 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"));
} }

View File

@ -1,5 +1,8 @@
package org.owasp.webgoat.plugin; package org.owasp.webgoat.plugin;
import java.util.HashMap;
import java.util.Map;
/** /**
* Created by jason on 1/5/17. * Created by jason on 1/5/17.
*/ */
@ -10,12 +13,10 @@ public class UserProfile {
private String size; private String size;
private boolean isAdmin; private boolean isAdmin;
private int role; private int role;
// anyting else?
public UserProfile() {} public UserProfile() {}
public UserProfile(String id) { public UserProfile(String id) {
this.userId = id;
setProfileFromId(id); setProfileFromId(id);
} }
@ -23,21 +24,35 @@ public class UserProfile {
private void setProfileFromId(String id) { private void setProfileFromId(String id) {
// emulate look up from database // emulate look up from database
if (id.equals("2342384")) { if (id.equals("2342384")) {
this.userId = id;
this.color = "yellow"; this.color = "yellow";
this.name = "Tom Cat"; this.name = "Tom Cat";
this.size = "small"; this.size = "small";
this.isAdmin = false; this.isAdmin = false;
this.role = 3; this.role = 3;
} else if (id.equals("2342388")) { } else if (id.equals("2342388")) {
this.userId = id;
this.color = "brown"; this.color = "brown";
this.name = "Buffalo Bill"; this.name = "Buffalo Bill";
this.size = "large"; this.size = "large";
this.isAdmin = false; this.isAdmin = false;
this.role = 3; this.role = 3;
} else {
//not found
} }
} }
public Map <String,Object> profileToMap () {
Map<String,Object> profileMap = new HashMap<>();
profileMap.put("userId", this.userId);
profileMap.put("name", this.name);
profileMap.put("color", this.color);
profileMap.put("size", this.size);
profileMap.put("role", this.role);
return profileMap;
}
public String toHTMLString() { public String toHTMLString() {
String htmlBreak = "<br/>"; String htmlBreak = "<br/>";
return "userId" + this.userId + htmlBreak + return "userId" + this.userId + htmlBreak +

View File

@ -2,7 +2,6 @@ package org.owasp.webgoat.plugin;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import org.owasp.webgoat.endpoints.AssignmentEndpoint; import org.owasp.webgoat.endpoints.AssignmentEndpoint;
import org.owasp.webgoat.endpoints.Endpoint;
import org.owasp.webgoat.lessons.AttackResult; import org.owasp.webgoat.lessons.AttackResult;
import org.owasp.webgoat.session.UserSessionData; import org.owasp.webgoat.session.UserSessionData;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -24,7 +23,7 @@ import java.util.Map;
*/ */
@Path("/IDOR/viewprofile/{id}") @Path("/IDOR/viewprofile/{id}")
public class ViewOtherUserProfileEndpoint extends AssignmentEndpoint { public class ViewOtherUserProfile extends AssignmentEndpoint {
private String color; private String color;
private String size; private String size;
@ -49,7 +48,8 @@ public class ViewOtherUserProfileEndpoint extends AssignmentEndpoint {
System.out.println("**** authenticated as " + userSessionData.getValue("idor-authenticated-as")); System.out.println("**** authenticated as " + userSessionData.getValue("idor-authenticated-as"));
//logged in //logged in
String authUserId = (String)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 //secure code would check to make sure authUserId matches userId or some similar access control
// ... and in this endpoint, we won't bother with that
UserProfile userProfile = new UserProfile(userId); UserProfile userProfile = new UserProfile(userId);
return trackProgress(AttackResult.failed("still working")); return trackProgress(AttackResult.failed("still working"));
} }

View File

@ -131,7 +131,7 @@
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson --> <!-- 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, <!-- 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 --> 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="adoc-content" th:replace="doc:IDOR_viewOtherProfile.adoc"></div>
<div class="attack-container"> <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 --> <!-- using attack-form class on your form, will allow your request to be ajaxified and stay within the display framework for webgoat -->
<div> <div>
@ -140,13 +140,13 @@
<!-- of course, you can write your own ajax submission /handling in your own javascript if you like --> <!-- 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 --> <!-- modify the action to point to the intended endpoint -->
<form class="attack-form" accept-charset="UNKNOWN" <form class="attack-form" accept-charset="UNKNOWN" id="view-other"
method="GET" name="form" method="GET" name="view-other-profile"
action="/WebGoat/IDOR/profile" action="/WebGoat/IDOR/profile"
enctype="application/json;charset=UTF-8"> enctype="application/json;charset=UTF-8">
<script th:src="@{/plugin_lessons/plugin/IDOR/js/idor.js}" /> <script th:src="@{/plugin_lessons/plugin/IDOR/js/idor.js}" />
<input name="View Profile" value="View Profile" type="button" onclick="onViewProfile();" /> <input name="View Profile" value="View Profile" type="submit" />
</form> </form>
</div> </div>
@ -155,6 +155,32 @@
<div class="attack-output"></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 --> <!-- ... of course, you can move them if you want to, but that will not look consistent to other lessons -->
</div> </div>
<div class="adoc-content" th:replace="doc:IDOR_editOtherProfile.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" id="edit-other"
method="GET" name="edit-other-profile"
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="submit" />
</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>
<div class="lesson-page-wrapper"> <div class="lesson-page-wrapper">

View File

@ -0,0 +1,8 @@
==== Edit Another Profile
Older apps may follow different patterns, but RESTful apps (which is what's going on here) often just change methods (and include a body or not)
to perform different functions.
Use that knowledge to take the same base request, change its method, path and body (payload) to modify another user's (Buffalo Bill's) profile.
Change the role to something lower (since higher privilege roles and users are ususally lower numbers). Also change modify the
user's color to 'red'.

View File

@ -19,7 +19,8 @@ POST, PUT, DELETE or other methods are also potentially succeptible and mainly o
== *Insecure* Direct Object References == *Insecure* Direct Object References
These are considered insecure when the reference is not properly handled and allows for authorization bypasses. These are considered insecure when the reference is not properly handled and allows for authorization bypasses or disclose private data that could be used to
perform opreations or access data that the user should not be able to perform or access.
Let's say that as a user, you go to view your profile and the URL looks something like: Let's say that as a user, you go to view your profile and the URL looks something like:
`https://some.company.tld/app/user/23398` `https://some.company.tld/app/user/23398`

View File

@ -0,0 +1,7 @@
=== Playing with the Patterns
==== View Another Profile
View someone else's profile by using the alternate path you already used to view your own profile. Use the 'View Profile' button
and intercept/modify the request to view another profile. Alternatively, you may also just be able to use a manual GET request with
your browser.