XSS Lesson Modifications (#367)

* initial cut on XSS, need to add some tests still

* initial unit tests for assignment endpoints

* updating header comment license thingy

* comment, clean up

* Stubs for security unit test

* Additional Unit Testing

* isEncoded and isNotEncoded Unit Tests added

* http-proxies updates

* update for XXE solutions

* Work-around to handle special chars in action ... currently to be able to match {userId} in hint creation/assignment for IDOR

* IDOR hints updated

* mitigation content update

* mitigation content update ... 2

* Lesson Overview updates

* including restart lesson fix for lesson overview
This commit is contained in:
misfir3
2017-07-10 08:33:10 -04:00
committed by GitHub
parent 3ec5b8708e
commit 82ef171a50
59 changed files with 1349 additions and 628 deletions

View File

@ -1,6 +1,7 @@
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.springframework.web.bind.annotation.RequestMapping;
@ -42,6 +43,7 @@ import java.io.IOException;
*/
@AssignmentPath("IDOR/diff-attributes")
@AssignmentHints({"idor.hints.idorDiffAttributes1","idor.hints.idorDiffAttributes2","idor.hints.idorDiffAttributes3"})
public class IDORDiffAttributes extends AssignmentEndpoint {
@RequestMapping(method = RequestMethod.POST)

View File

@ -1,6 +1,7 @@
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.session.UserSessionData;
@ -38,6 +39,7 @@ import org.springframework.web.bind.annotation.*;
*/
@AssignmentPath("IDOR/profile/{userId}")
@AssignmentHints({"idor.hints.otherProfile1","idor.hints.otherProfile2","idor.hints.otherProfile3","idor.hints.otherProfile4","idor.hints.otherProfile5","idor.hints.otherProfile6","idor.hints.otherProfile7","idor.hints.otherProfile8","idor.hints.otherProfile9"})
public class IDOREditOtherProfiile extends AssignmentEndpoint {
@Autowired

View File

@ -2,6 +2,7 @@ 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.session.UserSessionData;
@ -46,6 +47,7 @@ import java.util.Map;
*/
@AssignmentPath("IDOR/profile/{userId}")
@AssignmentHints({"idor.hints.otherProfile1","idor.hints.otherProfile2","idor.hints.otherProfile3","idor.hints.otherProfile4","idor.hints.otherProfile5","idor.hints.otherProfile6","idor.hints.otherProfile7","idor.hints.otherProfile8","idor.hints.otherProfile9"})
public class IDORViewOtherProfile extends AssignmentEndpoint{
@Autowired

View File

@ -2,6 +2,7 @@ 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.session.UserSessionData;
@ -46,6 +47,7 @@ import java.util.Map;
*/
@AssignmentPath("IDOR/profile/alt-path")
@AssignmentHints({"idor.hints.ownProfileAltUrl1","idor.hints.ownProfileAltUrl2","idor.hints.ownProfileAltUrl3"})
public class IDORViewOwnProfileAltUrl extends AssignmentEndpoint{
@Autowired

View File

@ -1,64 +1,66 @@
package org.owasp.webgoat.plugin;
import com.google.common.collect.Lists;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
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.RequestMapping;
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("/IDOR/viewprofile/{id}")
public class ViewOtherUserProfile extends AssignmentEndpoint {
private String color;
private String size;
private boolean isAdmin;
@Autowired
UserSessionData userSessionData;
@RequestMapping(produces = {"application/json"})
public @ResponseBody
AttackResult completed(@PathVariable String userId, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
List json = Lists.newArrayList();
// can be re-used
Map<String, Object> errorMap = new HashMap();
errorMap.put("error","not logged in, go back and log in first");
if (userSessionData.getValue("idor-authenticated-as") == null) {
json.add(errorMap);
return trackProgress(failed().feedback("idor.view.other.profile.failure1").build());
} else {
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 = (String)userSessionData.getValue("idor-authenticated-user-id");
//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);
return trackProgress(failed().feedback("idor.view.other.profile.failure2").build());
}
}
// else
return trackProgress(failed().build());
}
}
//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.RequestMapping;
//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("/IDOR/viewprofile/{id}")
//@AssignmentHints({"idor.hints.otherProfile1","idor.hints.otherProfile2","idor.hints.otherProfile3"})
//public class ViewOtherUserProfile extends AssignmentEndpoint {
//
// private String color;
// private String size;
// private boolean isAdmin;
//
// @Autowired
// UserSessionData userSessionData;
//
// @RequestMapping(produces = {"application/json"})
// public @ResponseBody
// AttackResult completed(@PathVariable String userId, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// List json = Lists.newArrayList();
// // can be re-used
// Map<String, Object> errorMap = new HashMap();
// errorMap.put("error","not logged in, go back and log in first");
//
// if (userSessionData.getValue("idor-authenticated-as") == null) {
// json.add(errorMap);
// return trackProgress(failed().feedback("idor.view.other.profile.failure1").build());
// } else {
// 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 = (String)userSessionData.getValue("idor-authenticated-user-id");
// //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);
// return trackProgress(failed().feedback("idor.view.other.profile.failure2").build());
// }
// }
// // else
// return trackProgress(failed().build());
// }
//
//
//
//
//}

View File

@ -138,7 +138,7 @@
<!-- modify the action to point to the intended endpoint -->
<form class="attack-form" accept-charset="UNKNOWN" id="view-other"
method="GET" name="view-other-profile"
action="/WebGoat/IDOR/profile"
action="/WebGoat/IDOR/profile/{userId}"
enctype="application/json;charset=UTF-8">
<script th:src="@{/lesson_js/idor.js}" />
@ -163,7 +163,7 @@
<!-- 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"
action="/WebGoat/IDOR/profile/{userId}"
enctype="application/json;charset=UTF-8">
<script th:src="@{/lesson_js/idor.js}" />

View File

@ -1,7 +1,24 @@
idor.title=Insecure Direct Object References
idor.hints.idor_login=Log in first
idor.hints.idor_login=Log in first. User Name is tom, password is cat.
idor.hints.idorDiffAttributes1=Make sure you have logged in on the previous step/page
idor.hints.idorDiffAttributes2=View the response using developer tools or a proxy.
idor.hints.idorDiffAttributes3=The attributes are not visible and have nothing to do with size, color or name
idor.hints.ownProfileAltUrl1=Look at the previous request for profile, this is similar
idor.hints.ownProfileAltUrl2=You will need data from the previous request for your own profile
idor.hints.ownProfileAltUrl3=Append your id to the previous request (i.e. .../profile/{yourId})
idor.hints.otherProfile1=The default request here won't work at all, so you will need to manually craft the request or tamper it with a proxy
idor.hints.otherProfile2=You will likely need to 'fuzz' to try different values for the userId at the end of the Url
idor.hints.otherProfile3=Try incrementing the id value. It's not a simple +1, but it's also not too far off
idor.hints.otherProfile4=For editing the other user's profile, you will need to use the proxy or manually craft the request again
idor.hints.otherProfile5=To edit the other user's profile, you will use the same Url you did to view the other user's profile
idor.hints.otherProfile6=To edit, You will need to change the method, what is the RESTful method used for 'update' or 'edit'?
idor.hints.otherProfile7=You will also need the body of the request (will look something like the profile)
idor.hints.otherProfile8=The request should go to ... /WebGoat/IDOR/profile/{Buffalo Bills Id}
idor.hints.otherProfile9={\"role\" : 1,\"color\" : \"red\",\"size\" : \"small\",\"name\" : \"Tom Cat\",\"userId\" : \"2342388\"}
idor.diff.attributes.missing=You did not list two attributes, comma delimited
idor.diff.success=Correct, the two attributes not displayed are userId & role. Keep those in mind

View File

@ -1,10 +1,58 @@
== Secure Object References
=== Start with the end in mind
Do you have your access control documented? If you don't, how can you enforce it? Access control is defined
by the business logic that guides the application and/or privacy and other laws.
==== Horizontal and Vertical Access Control
Often times we think of access control in terms of 'roles' (user, power-user, admin, etc.).
However, as noted in the previous exercises, users with the same 'role' can access each other's data. This is
horizontal access control. Both should be enforced.
.Access Control Matrix Example
|===
|Endpoint | Method | Description | Roles, Access Rules | Notes, Caveats
| /profile
| GET
| view user profile
| Logged in User, can only view their own role
| Admin roles must use diff Url to view others' profiles (see below)
| /profile/{id}
| GET
| view user profile of a given user
| Logged in User can view their own profile by {id}, admins can also view
| n/a
| /profile/{id}
| PUT
| edit user profile. profile object submitted from client with request
| Logged in User can edit their own profile by {id}, admins can also edit.
| Admin edit must be logged
|===
==== Audit Access
As displayed in the above example, your access control rules should include provisions of what access is logged.
For example, if a super-user or admin can edit other's profiles ... That is something that should be logged. Other
examples would include detected violations or attempts to violate access control mechanisms.
=== Using Indrect References
In some cases, using an indirect reference may help. This of course may make the application harder to trouble shoot and may also affect performance.
Not many applications employ it, but you can use *indirect* refrences. In this case you can run your references across a hahsing,
encoding or other function on the server so that the id that the client sees is not the actual reference
which the server handles. This will reduce efficiency some (a common trade-off for security) and is still subject to being
guessed, brute-forced or reverse engineered.
=== Access Controls
Since the root of 'Insecure' of IDOR's s really in controlling access to the objects referenced, the key to making them secure is implementing vertical and horizontal access control.
This approach should not be the only protection used. It can be used as an additional layer. Your server must
implement the logic of mapping client (indirect) to server (direct) references.
==== Knowing How Things Should be Secured
=== Access Control & APIs
Many time, APIs or RESTFul endpoints rely on obscurity , a static 'key', or lack of imagination on the user's part to control access.
Good options such as digitally signed JSON Web Tokens (https://jwt.io) are a good option for API authentication & access control using a
combination of the claims and a digital/cryptographic signature to validate the consumer. Other emerging standards such as
Secure Token Binding promise a 'cryptographic state' for web services in the request headers ...
https://tools.ietf.org/html/draft-ietf-tokbind-protocol-10
https://tools.ietf.org/html/draft-ietf-tokbind-negotiation-05
https://tools.ietf.org/html/draft-ietf-tokbind-https-06