Home Multi-Factor Authentication (MFA)
Post
Cancel

Multi-Factor Authentication (MFA)

Intro

This post/writeup is all about the Authentication vulnerabilities or Broken Authentication if we follow OWASP naming scheme.

I’ll be using primarily Portswigger Web Academy Labs, but i do intent do throw other labs and writeups here as well.

2FA (two-factor authentication) is based on something you know and something you have and should be implemented in a way so they check the same factor in two/more diferent ways.

TOC

2FA simple bypass

This lab’s two-factor authentication can be bypassed. You have already obtained a valid username and password, but do not have access to the user’s 2FA verification code. To solve the lab, access Carlos’s account page.

Your credentials: wiener:peter

Victim’s credentials carlos:montoya

Bypassing the 2FA through bad auth. implementation

If we login as wiener:peter we recieve an Email.

picture 69

If we enter the 4-digit code, we’d get to my-account page.

Problem with this applications authentication implementation is that when we’ve entered the username:password we’re already logged in thus skipping the 2FA is entirely possible.

If we enter carlos:montoya we’d get asked for 4-digit code BUT if we then just go to /my-account we would have bypassed that step.

If we do just that as described, we’d solve the lab!

picture 70

2FA broken logic

This lab’s two-factor authentication is vulnerable due to its flawed logic. To solve the lab, access Carlos’s account page.

Your credentials: wiener:peter

Victim’s username: carlos

You also have access to the email server to receive your 2FA verification code.

Vulnerable 2FA broken logic Enumeration

Let’s first login using known working credentials wiener:peter.

After entering credentials we have to enter 4-digit code which we can retrieve from Email client with a message like Hello! Your security code is 1731..

Let’s inspect both requests now in Burp.

1st Request to /login

picture 73

2nd Request to /login2

picture 71

There is an unusual cookie verify=wiener.

If we send GET request to login2 to Burp Repeater. Now few Questions arise:

  • Can we simply ask for 2FA token? We should recieve a new code if we use repeater
  • Can we also do it for carlos? We shouldn’t recieve any new code when we use repeater => GET Request to /login2
  • Can we bruteforce the code? Will we be redirected to carlos’s my-account or back to login or will we see any errors?

Vulnerable 2FA broken logic Exploitation

Let’s ask for token for carlos.

picture 74

Send to Turbo Intruder:

picture 75

We know that we need 302 as result, considering how application reacts on correct MFA-code.

picture 77

Code used in Turbo Intruder. Only digits to 3000 will be used, but it could’ve been set to 9999

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Find more example scripts at https://github.com/PortSwigger/turbo-intruder/blob/master/resources/examples/default.py
def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint,
                           concurrentConnections=3,
                           requestsPerConnection=4,
                           pipeline=False
                           )
    for i in range(3000):
        engine.queue(target.req, '{:d}'.format(i).zfill(4))
        

@MatchStatus(302)
def handleResponse(req, interesting):
    if interesting:
        table.add(req)

There was a hit in ca 45 seconds.

picture 78

Code was 0810.

If i open same request in the Browser, we’re logged in as Carlos

picture 79

2FA bypass using a brute-force attack

This lab’s two-factor authentication is vulnerable to brute-forcing. You have already obtained a valid username and password, but do not have access to the user’s 2FA verification code. To solve the lab, brute-force the 2FA code and access Carlos’s account page.

Victim’s credentials: carlos:montoya

2FA Enumeration

Since 2FA mechanismus is pretty much the same as in the previous 2 exercises i’ll just describe what happens:

  1. POST Request to login with username and password
  2. We land on login2 where we have to enter 4-digit token.

picture 80

Now if there is no Brute-force defense mechanismus on 4-digit PIN we will be able to brute-force it, however we need to bypass CSRF Token as well. After 2 retries we’ll be sent back to login where we need to enter password again.

picture 81

In order bypass CSRF and auto-logout we need to automate the following:

  1. Get Request to /login. Capture the CSRF here
  2. POST Request to /login
  3. GET /login2
  4. POST /login2 with CSRF.

picture 82

2FA Exploitation

We can use Project Sessions with Macro that would help us retrieve new CSRF token. As already mentioned, this would be the requests that we need:

picture 83

If we do a test run, we can see that we’re asked for 4-digit code and CSRF token seems to be present as hidden field.

picture 84

Macro has now been set up:

picture 85

Rule Settings were changed as i’ll be using Target Scope AND Extender for Turbo Intruder picture 86

Now if we start Intruder, CSRF token should get changed automatically so We don’t need to worry about CSRF and/or do anything with it.

picture 88

Payload was set as following using Numbers:

picture 89

Remember, we would see HTTP Error 400 if CSRF token would not match, so brute-force works!

picture 87

This would also work with Turbo Intruder. I’ve changed script as following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Find more example scripts at https://github.com/PortSwigger/turbo-intruder/blob/master/resources/examples/default.py
def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint,
                           concurrentConnections=1,
                           requestsPerConnection=1,
                           #pipeline=False,
                           engine=Engine.BURP
                           )
    for i in range(9999):
        engine.queue(target.req, '{:d}'.format(i).zfill(4))
        

@MatchStatus(302)
def handleResponse(req, interesting):
    if interesting:
        table.add(req)

There’s a PIN

picture 90

I haven’t thought of that, but attack should’ve been stopped as soon as HTTP 302 was returned.

Anyways, the lab has been solved!

picture 91

This post is licensed under CC BY 4.0 by the author.