The onslaught of credential stuffing attacks, through which attackers take leaked username-password combinations from one service and attempt to use them elsewhere, has pushed companies to implement multi-factor authentication (MFA) faster than ever. That’s good news for the people using these services, but it also means attackers looking to compromise user accounts are increasingly keen to find ways to bypass MFA.
One of the most common MFA bypass techniques is a response tampering attack. Response tampering attacks happen when only client-side security controls are forced for user and server-side security checks do not conduct properly at the back end. If a security control is handled at the client-side code, it can be manipulated easily. This technique is dangerous because it’s caused by a logical error in the software being exploited, which means it can only be detected via manual testing by experts.
In this blog, I’ll cover two real-world MFA bypass techniques via response tampering that I have found in the Synack Platform throughout my time as a member of the Synack Red Team, starting from the easiest to more complicated.
Easy Exploitation
One Node.js-based application was using MFA confirmation for users registering for new accounts on mobile devices. After the form was sent, the app would send a one-time password (OTP) to a mobile number entered by the user. If the wrong OTP was entered, the app returned an error.
Analyzing back-end requests showed that the application returned a “400 Bad Request” error with a body containing two parameters, “size” and “timeout,” both were set to “0.”
This could be bypassed using a response tampering attack. All I had to do was change the HTTP status response code from “400 Bad Request” to “200 OK” and set the “size” parameter to “1” from “0”. Since client-side code on the web browser were only checking whether response had returned size parameter as “1” to control success/fail MFA conditions, and were not saving the actual state of the MFA at the back end, it was possible to bypass the client side security controls within tampering the back end responses coming to the web browsers.
Hard Exploitation
Another app used MFA as an additional security control during the login process. When choosing an already-added method of receiving OTPs, the app would redirect users to a page where they could enter the code.
If an invalid OTP was entered, the app would return an error response, as seen below:
If the correct OTP was entered, the app returned the following:
Comparing the responses offered a clue as to where to find a response tampering attack vector. In this case, it seemed obvious. If the “actionResult” parameter contained the value “challenge,” it returned users to the MFA page, but if it contained “success” users were redirected to the next step in the login process. But it was only in theory.
Unfortunately for me, it wasn’t that easy. Because some other parameters were kept at “localStorage” within the browser, the app would redirect to the login page if the “actionResult” parameter was changed. However, copying and pasting the full response of the success message at the failed code request would allow attackers to bypass the MFA confirmation. This showed that cookies generated for the session did not check to see if the OTP was correct or incorrect on the back end. Instead, it trusted client-side controls only, which can be tampered with.
Because the successful response body didn’t have a complex value to guess, such as a session cookie or OTP token, which is checked later, it was possible for already-authenticated attackers to enumerate this response by repeating the process and using the gathered info for further attacks.
Ozgur Alp is a member of the Synack Red Team.