The Critical Need for Multi-Role Testing in Application Security
Posted by: Austin Turecek
As web, thick client, mobile, and IoT applications have become more robust, authentication and authorization has become an incredibly complex and sophisticated problem. With the continued development of new integrations and frameworks, application-level controls—as they relate to authentication and authorization—can become as robust and complex as large computer networks.
With basic off-the-shelf tooling, developers can quickly create custom authorization controls, allowing for expansive use cases. Of course, these developments come with increased risk. With the introduction of multi-level role types comes the introduction of privilege escalation. And with the introduction of unique user controls comes the introduction of lateral movement attacks.
While this is widely understood, it is still very common for organizations to limit or altogether forgo multi-role testing purposefully.
Authentication issues are an everyday occurrence.
Despite how common and well-documented issues such as privilege escalation and lateral movement are within applications, it’s not uncommon for development teams to limit testing to individual roles. There are a variety of reasons why an organization may choose to focus its testing on only a single role. However, it is incredibly important that these decisions are made with the correct resourcing and focus.
Typical reasons why organizations don’t use Multi-Role Testing:
“Our admin accounts are used by trusted internal resources only.“
Many organizations have taken numerous steps to protect their crown jewel high-privilege accounts. Access may be restricted to internal use only; it may employ additional controls such as further MFA and may make use of more stringent password policies. While these controls are incredibly important, they cannot be considered foolproof.
Threat actors continually become more sophisticated with their tooling. Things like password stealers, for example, have expanded far past just that. Instead of simply stealing a username and password, threat actors now employ sophisticated stealers capable of stealing entire browser fingerprints. This includes not just passwords but also information about the computer, down to things as minute as installed fonts.
These browser fingerprints create an increasingly sophisticated and difficult-to-detect tool that can bypass even the most sophisticated authentication mechanisms.
Given these ever-improving tools, it is now more important than ever to ensure that all levels of user accounts are properly tested and secured.
“We plan on having two separate tests. One to look at privileged accounts, the other to look at low privileged accounts.“
As application penetration testers, we love to test apps—the more, the merrier. However, we also love to ensure that we perform as extensive of a test as possible—quality over quantity.
Testing different permission sets in different tests is a great way to see what risk a specific user type poses to the application, environment, and its data. However, when tests of this nature are split apart, an important bit of context is lost; applications rarely exist in a state of static engagement. Users interact with the app, and the app’s state changes, and that, in turn, causes users to change the way they interact.
Additionally, actions taken by one user may lead to a response from another user. Separating individual account types into separate, segmented tests removes a core feature of these types of applications.
It’s important when testing to ensure that the app is as close to the real-world operation it will experience. Additionally, separating tests for this purpose greatly restricts a tester’s ability to probe and analyze the intended access controls fully.
“Administrative accounts have access to highly sensitive features which we can’t expose.”
Admin accounts, by definition, are powerful. That is to be expected. Threat actors understand this as well. As such, high-privilege accounts are regularly targeted. Without a proper review of these accounts, their role in the application, and the application of access controls, organizations leave their environments in an unpredictable and precarious state.
When testing applications, GuidePoint Security aims to avoid interacting with disruptive and dangerous features within our applications. To help ensure this, we break our testing into two portions. Over eighty percent of our testing is aimed at manually analyzing the application, network traffic, and fuzzing to identify notable vulnerabilities through passive and active means. By doing this, we can ensure we avoid engaging with disruptive components and that our automated tools do not interact with sensitive and controlled features.
Auth issues in the wild
Given the complexity of authorization and authentication, these types of findings constitute the most common vulnerabilities we identify. This shouldn’t come as much of a surprise as “Broken Access Control” and “Identification and Authentication Failures” constitute two of the OWASP Top 10.
Despite how common these are, it is never a bad idea to explore some of the most common issues we find in our assessments.
Lateral Movement Issues
Falling under both authentication and authorization failures, insecure direct object references (IDOR) remain one of the most common access control issues. In many cases, the existence of these findings is due to a reliance on obscurity.
When creating and storing sensitive content, developers may create a generic ID value that the application can reference. These IDs can be file names, alphanumeric values, or even raw numbers. By choosing a large pool of identifiers, a developer may feel that the data is secure. It is worth noting that while unique identifiers like GUIDs may appear to reduce the overall risk, the application’s risk exposure is only lessened so long as the GUIDS themselves aren’t leaked: Security through obscurity is rarely a good security solution.
Unfortunately, these types of issues can be exploited trivially, especially when using numerical identifiers or easy-to-find identifiers like emails.
One of many examples discovered by GuidePoint can be seen below. (Please note that records have been changed to protect client confidentiality.)
REQUEST:
GET /mobileapi/GetConfigurations?&id=[REDACTED] HTTP/1.1
Host: [REDACTED]
Authorization: Bearer [REDACTED]
Accept-Encoding: gzip, deflate, br
User-Agent: [REDACTED]
Connection: close
RESPONSE:
HTTP/1.1 200 OK
Date: Tue, 14 Nov 2023 01:16:52 GMT
Content-Type: application/json
Content-Length: 736
Connection: close
{"APIToken":"[REDACTED]", "FirstName":"[REDACTED]", "LastName":"[REDACTED]","Permisions":"[REDACTED]","UserId":"[REDACTED]","Version":"1"}
If this seems simple to exploit, it’s because it is. Threats like this are relatively common and, if exploited, can have a tremendous impact. While this instance required authentication, the lack of sufficient authorization meant that low-level users could cause significant harm to other users.
Through simple automation, an attacker can quickly and easily enumerate numerous valid identifiers and dump sensitive information.
By having multiple accounts, vulnerabilities of this nature become much easier to test. Without the guesswork of finding a valid ID belonging to another user, testers can focus on the root issues and more effectively report said issues.
Lateral attacks of this nature can undermine trust in the application, lead to sensitive data theft, and even full-on account takeovers depending on what type of actions the IDOR is taking.
Privilege Escalation Issues
In any multi-level user account system, the threat of privilege escalation poses a notable risk. Testing for these issues without multiple roles, however, proves a significant challenge. While we, as testers, can sometimes identify sensitive, privileged endpoints via careful enumeration or exposed content via client-side code, there is no guarantee this exposure will exist during an active test.
While most tests are classified as point and time assessments, multi-role testing helps to remove much of the guesswork. This means a tester has a much higher chance of identifying potential privilege escalation issues before an attacker does, making your application more robust in the future.
This means that if there were an exposure via client-side code or some type of leak, an attacker would be far less likely to exploit an escalation issue, as the issue would have already been identified.
To highlight this, we have an example of an escalation issue identified in a multi-role test.
In this assessment, GuidePoint reviewed an order portal. This portal was designed to handle multiple user types and customer and employee accounts. Given the outside role of customers, these accounts were specifically designed not to have access to certain functions.
Looking through the client-side code, GuidePoint found that the organization did an amazing job at separating each functionality on a per-role basis. Attempts to brute force files and directories also returned very little. This meant that each account was only aware of endpoints related to its role.
After logging in with an elevated account, GuidePoint found a request sent to the following privileged endpoint. The intent was to retrieve a list of invoices with issues/errors for employee accounts to review. (Please note that records have been changed to protect client confidentiality.)
REQUEST:
GET /portal/privileged/InvoiceHistoricalError/search HTTP/1.1
Host: [REDACTED]
Cookie: user=privileged_test; [REDACTED]
...SNIPPED...
RESPONSE:
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Length: [REDACTED]
Server: Apache
Pragma: no-cache
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Cache-Control: no-cache
Cache-Control: no-store
Content-Language: en-US
Vary: Accept-Encoding
...SNIPPED...
<div class="formContainer">
<div class="head-wrapper">
<h1>
Search / Invoice Historical Errors
</h1>
</div>
While the “portal” directory was easily identifiable with directory brute forcing, the ‘InvoiceHistoricalError” was unique enough that it likely would not appear in any word list.
However, thanks to the multi-role test we were running, it was easy to identify. By simply replaying the account with a low-level “customer” account and leveraging a “forced-browser” attack, it was uncovered that customers had access to this endpoint and the confidential data it housed.
In the application’s current state, this vulnerability would have likely been difficult for an attacker to identify. However, the impact of successful exploitation could have been disastrous; if the application was ever to be updated and client-side code consolidated, for example, the exposure of this endpoint would have spelled disaster. Thanks to multi-role testing, however, the vulnerability was trivial to identify, and the application was left in a far more secure state.
Conclusion
Multi-role testing is an important part of the application development lifecycle. Tests performed without this unique perspective run the risk of creating blind spots and allowing trivially exploitable vulnerabilities to persist and threaten an application.
While vulnerabilities uncovered through these means of testing may be difficult to exploit in the application’s current form, future changes to an app may expose and threaten the confidentiality, integrity, and availability of data.
In many ways, multi-role testing is an important step when developing applications with the Defense in Depth mindset. While it is true that a vulnerability may be difficult to find without multiple account types, obfuscation/hidden knowledge should never be the foundation of an application’s security. While purposefully limiting what information a user has access to helps reduce an application’s attack surface, there are a variety of different events that may lead to leaks and exposures of operational knowledge.
For an ideal secure solution, applications should be tested from every perspective to ensure that operational leaks are low impact and pose little risk to an application’s data.