This blogpost continues the saga of articles on problems in Web and/or mobile applications. Our goal is to reinforce that it is always possible to apply several layers of security to protect your company/business. Thus, we will start a series of blogposts where we will discuss some of the main problems of bad implementations – from the point of view of security – of business rules; data validation; multiple stages; registration procedure; fingerprints and facial recognition; subversions with calls to activities in Android applications; in addition to problems related to secret questions and answers.
More specifically in this article, we’ll introduce some issues related to multi-factor authentication. Not necessarily problems in the MFA itself, but actually, the security issues encountered in the implementations used in current applications.
For those who do not know or have never heard of, multi-factor authentication / authorization (aka 2FA, MFA, two-factor authentication or even two-step authorization) consists, in short, of a simple security measure made up of an extra layer of protection, requiring another form of authorization to access a particular account or perform a particular action. For example, suppose you have 2FA enabled in your email account. To access this account, it’s necessary to enter your login and password (the password, theoretically, being the first authorization factor). In addition, shortly after submitting the email and its password, a verification code will be requested which was sent to some other means of communication of your possession (SMS, another email, a call, etc.) or generated through applications with this purpose (such as Google Authentication, Authy, among others) in order to make it possible to continue the access. Thus, there are five different types of factors that can be used to perform such validation, which are:
- Something you are: facial recognition, iris scan, fingerprint;
- Something you have: hardware or software tokens;
- Something you know: password, PIN;
- Something you do: signature by hand;
- Where you are: current location, IP source location.
This simple extra layer tries to prevent unrestricted and unauthorized access to a particular service, in case your credential has been compromised. It’s worth remembering that this doesn’t prevent the attacker from trying the same credential on different services. Therefore, it’s recommended not to use the same password for different services.
Given the simplicity of this model, it may seem a little difficult to imagine some problems related to it. However, the most common problems with MFA happen when integrating the application with this system.
Unfortunately, it’s quite common for applications to use the SMS (short message service) service as a means of sending MFA code. This type of service is flawed (in terms of security) by default, due to several factors that will not be addressed in this text. In addition, this service has a relatively poor infrastructure, where, for example, a given application may present the message sending the code to the previously registered phone number, but the code may take a long time to be received – making access impossible due to some expiration time applied by the application – or even it never reached the recipient.
Due to reasons like this, not every application or business makes the decision to make a more robust 2FA feature available, due to user experience problems and the cost related to its maintenance. Besides, it’s quite common to find applications that have security holes in this integration: either due to the absence of any validation; either in the way the code (or token) was generated; or in the way this code was sent to the user.
To try to explain a little more about the security problems in these integrations between the application and 2FA, let’s see some illustrative scenarios, but which have already been detected in real applications, starting from the simplest to the most complex.
Case 1 – Is it true or false?
A fictitious bank that we will call the SideChannel Bank launched its new internet banking – both for the mobile platform and for the Web platform. After serious problems with fraud, the novelties of the new system consist of improvements in the security of the application, in order to prevent unrestricted access to the account, which now requires the release (or authorization) of access to the account holder. For that, some methods can be adopted, such as sending a code by SMS to the cell phone associated with the account; the requirement to unlock at an ATM, on one of the devices with access to the account; or the confirmation of personal information such as the name of the mother, pet or team of the heart (commonly known as “secret answers”).
For this first case, we will focus only on the authentication functionality in conjunction with the application’s 2FA, and ignore any other type of security problem, be it user enumeration or brute force attacks.
The bank’s main attacker paid attention to the update and decided to analyze how the new functionalities applied were behaving. At first, he realized that the application’s authentication request behaves as follows:
After the request, the Web application presents a screen asking for a 6-digit code that was sent to the account holder’s phone, as illustrated in the following image:
After a brief analysis, the attacker re-authenticates, intercepts the application’s response and modifies the 2FA field, changing the value from true to false, in order to release the response to the browser. From there, the application no longer presents the code request screen, but the main screen of the internet banking, with the authenticated account, all data loaded normally and also all the available features.
Once this is done, the attacker decides to analyze the client-side code of the application, since any change in the application’s behavior after modifying a response leads to the belief that validations are being performed on the client side, and not on the server. The code used in the application can be seen in figure 2:
The attacker verified that the validation happens through the flag returned by the application, according to the code block between lines 13 and 17: if the flag comes from the server as true, the application will present the code request screen.
Given the code shown in figure 2, it is possible to see that the responsibility for knowing whether or not it is necessary to submit the 2FA request is on the client side. And all of this is done through a flag returned by the server, whose value can be ‘true’ or ‘false’.
To better illustrate the scenario, figure 3 shown below, similarly to how it was demonstrated in authentication, presents the request made to the server, as well as its response when using the 2FA code submission functionality. For the aforementioned request, the code sent is valid, since the server returned the value true for the success field. Note that the application behaves practically the same as the authentication functionality:
Observing the authentication request shown in figure 1, we see that the server already returns a valid session token even before the verification of 2FA; and that the 2FA functionality doesn’t return any other new tokens. Therefore, we can infer that the same implementation of session creation was used to free access to accounts that have 2FA and accounts that don’t.
Putting all this information together, we can assume that the application’s authentication functionality code may look something like the code illustrated in figure 4:
This type of implementation is commonly found in current applications, and put its users at risk due to its ease of subversion. Promptly, as already demonstrated, it is possible to observe the subversion in the 2FA request. In addition to this, it is very likely that the same subversion procedure can also be performed when sending the 2FA code, since normally these codes, like the excerpt illustrated above, are reused several times in the same application, so that the system behaves very similarly between the two mentioned features.
Repeating the process in the authentication functionality, and performing some other tests, we can infer that the code of the 2FA functionality works as follows:
Taking this code into account, it’s noticed that, at no time, the actions described below are performed by the application in its authentication operations or in the 2FA challenge validation:
- Perform the access control check to the application pages on the server-side;
- Check if the code sent really belongs to the user who sent it;
- Check if the code sent has already been used at another time;
- Remove the code sent, if it’s valid;
- Generate a session token after 2FA validation and remove the existing one, confirming that that user actually responded to 2FA correctly.
The points described above are of great relevance for a correct implementation of 2FA, thus forming a list of good security practices to prevent the subversion of such a mechanism.
Case 2 – No button, no access
Continuing with our SideChannel Bank, let’s see an attack scenario for the mobile application.
In addition to the functionality already mentioned in 2FA via SMS existing in the Web application, the mobile application has a security feature that aims to prevent unrestricted access to the account, requiring the release of the device in an ATM for its first access. This means that, as long as the device is not released / unlocked in any agency, it will not be possible to use the actions of the mobile application. After unlocking, a code for 2FA validation will be sent via SMS to release the remaining accesses on the device already identified. The following image illustrates the behavior of the application when accessing an account through a device that has not yet been released:
A common mistake in this case, is to assume that, due to the fact that the application does not offer any option other than the exit button, the step presents no danger to the business. As if the only option in this case was to give up and leave. However, this type of thinking ends up generating several security problems, as in the case of the screen/activity.
In a very brief way, according to Android’s own documentation, “unlike the programming paradigms in which apps are launched with a main() method, the Android system starts the code in an Activity instance invoking callback methods that correspond to specific stages of the life cycle.”
There are several ways to “pull” another screen / activity in addition to the user interface (UI) itself. For example, it is possible to “call” another screen / activity to perform the actions available on it through function hooks, using, for example, the objection¹ toolkit, developed for mobile exploration at runtime, with the purpose of evaluating the security posture of mobile applications.
Therefore, using the aforementioned tool, it is possible to list the activities of the application, and thus, call any activity using the following command:
For the SideChannel Bank scenario, the activity was called using the following command:
With this, it was possible to access any and all functionality available in the application, without the need to unlock the ATM, as shown in the images below:
This problem has the same characteristic as the problem mentioned in the authentication process, where, after this process, the application provides a valid session token so that the user can perform any and all actions before even performing another validation.
There are several relatively simple solutions that aim to prevent the problems discussed here. First, knowing the possibility of the attacker having full control to subvert the aforementioned protections, it’s essential to remove any and all validation of access (or authorization) from the client-side for critical operations of a system. This solution must be generalized for all cases, whether for validation of 2FA (or MFA), or even for authentication or validation of a certain action. This way, you can prevent, for example, that a flag is changed in the response of the request from false to true (and vice versa); which, as we’ve seen, compromises the application.
This validation can be done through user session tokens. For example, after submitting the correct 2FA code to the server, it must create a new token, return it to the user and remove the previous one. Or, even, only make a session token available after the correct submission of the 2FA code. This new session token will have the characteristic of allowing the user to perform the requested action. Once the action is completed, the token must lose the function of authorizing the requested action.
In addition, it is extremely important that any factor of authentication and/or authorization, despite its specifications and particularities, follows the following concepts:
- Always validate the MFA challenge on the server side;
- Be for single-use (after use, it must be discarded);
- Have a limited validity time (ranging from 60 to 120 seconds);
- Don’t be predictable.
It’s also worth mentioning that, when implementing an MFA, it’s advisable to use combined factors (for example, something you know, plus something you have). Besides, good security practices for the chosen technology should always be followed.
Fingerprint solutions can also be quite effective against scenarios of fraud or subversion of the rules of a business, since the mapping of these actions is easily “blocked” after the identification of inappropriate uses.
Using fraud engines, WAF, or any other tools helps to prevent attacks against the system. Even so, despite creating more layers of protection, they do not fully protect, nor do they solve all existing problems. For example, if the application allows the user to select an extremely large number of installments in their purchase, it’s very likely that these tools will not be able to detect such an action and therefore cannot prevent it.
Additionally, monitoring the actions taken in the application is also usually a very efficient approach for detecting fraud in the system. Along with assisting in incident response processes, it assists in reevaluating application protection layers.
Since there are still many problems related to MFA implementations (even in the creation of the code), so as not to extend too much in a single blogpost, other business rule problems will be presented in the continuation of this series.
We remind you that, although the codes presented here are merely illustrative, it’s quite common to find this type of problem in real applications nowadays, both in small and large corporations. That’s why, regardless of the tools used (such as WAF, fraud engines, etc.) it’s extremely important to perform security validations, in the environments used and, in the applications, on a recurring basis; or even planning a security cycle already in the wake of development. And of course, don’t decide to do security validations/tests only after being attacked. And if, even after reading this text, you still feel that it’s not worth to implement an extra layer of security, for fear of compromising usability and user experience, since “usability and user experience walk in the against the hand of security” (word on the streets), it’s recommended to read this blogpost here.
The Web Application Hacker’s Handbook – Second Edition