cai/fluency/my-first-hack/utils/portswiggerbot.py

215 lines
7.3 KiB
Python
Executable File

#BOT
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support import expected_conditions as EC
import time
import random
import json
from pathlib import Path
class Bot():
def __init__(self,headless=True):
"""
Initializes the MyBrowser instance.
Sets up Chrome WebDriver with headless mode and necessary arguments
for a minimal and secure browsing session. Also defines login and labs URLs.
"""
self.LOGIN_URL = 'https://portswigger.net/users'
self.LABS_URL = 'https://portswigger.net/web-security/all-labs#'
self.prefixes_filename = 'topics_prefixes.json'
self.options = Options()
if headless:
args = ['--headless','--disable-gpu', '--no-sandbox']
else:
args = ['--disable-gpu', '--no-sandbox']
for arg in args:
self.options.add_argument(arg)
self.driver = webdriver.Chrome(options=self.options)
def __wait_random_time(self,min_seconds=3, max_seconds=5):
"""
Waits for a random amount of time between min_seconds and max_seconds.
Args:
min_seconds (int, optional): Minimum number of seconds to wait. Defaults to 3.
max_seconds (int, optional): Maximum number of seconds to wait. Defaults to 5.
"""
duration = random.uniform(min_seconds, max_seconds)
time.sleep(duration)
def login(self,username,password):
"""
Logs in to the PortSwigger user portal using the given credentials.
Args:
username (str): The email address or username for login.
password (str): The corresponding password for the user account.
Opens the login page, waits a random time, then fills and submits the login form.
"""
#Open the login page
self.driver.get(self.LOGIN_URL)
#Wait for the page to load
self.__wait_random_time()
#Find and fill in the email field
email_input = self.driver.find_element(By.ID, "EmailAddress")
email_input.send_keys(username)
#Find and fill in the password field
password_input = self.driver.find_element(By.ID, "Password")
password_input.send_keys(password)
#Submit the login form
password_input.send_keys(Keys.RETURN)
#Wait for the page to load
self.__wait_random_time()
def choose_topic(self,topic_name='cross-site-scripting',level=None):
"""
Extract urls of each of the labs in the selected section.
Args:
topic_name (str): the name of the topic.
Read topic prefixes files, extract links of labs based by topic section (topic_name) and returns a list of lab urls.
"""
#Read topic prefixes json file and get prefix for topic_name
current_folder = Path(__file__).parent
available_topics = json.loads(open(f'{current_folder}/{self.prefixes_filename}').read())
#If topic_name does not exists then returns empty list
try:
topic_prefix = available_topics[topic_name]
except KeyError:
print(f"Topic '{topic_name}' not found")
return []
#Go to sections urls
self.driver.get(f'{self.LABS_URL}{topic_name}')
self.__wait_random_time(min_seconds=5, max_seconds=7)
links = WebDriverWait(self.driver, 10).until(
EC.presence_of_all_elements_located((By.CLASS_NAME, 'widgetcontainer-lab-link'))
)
#Find all <a> elements that have the topic prefix in the href
links = self.driver.find_elements(By.CLASS_NAME, 'widgetcontainer-lab-link')
#Extract the href attributes
if level:
extracted_links = [link.find_element(By.TAG_NAME, 'a').get_attribute('href') for link in links if link.find_element(By.TAG_NAME, 'span').text == level]
else:
extracted_links = [link.find_element(By.TAG_NAME, 'a').get_attribute('href') for link in links]
#Filter links that contain the topic prefix
return [link for link in extracted_links if topic_prefix == link.split('/')[4]]
def obtain_lab_information(self,lab_url):
"""
Extract the information associated to a lab url.
Args:
lab_url (str): the url of the lab.
Extract the information associated to the lab such as Title, Description, Solution and Environment url.
"""
#Go to lab url
self.driver.get(lab_url)
#Extract type of lab from url
labtype = lab_url.split('/')[4]
#Extract title of the lab
title = self.driver.find_element(By.CLASS_NAME, 'heading-2').text
#Extract description of the lab
description_section = self.driver.find_element(By.CLASS_NAME, "section.theme-white")
paragraphs = description_section.find_elements(By.XPATH, ".//p[following-sibling::div[@class='container-buttons-left']]")
#Extract solution of the lab
solution_sections = self.driver.find_elements(By.CLASS_NAME, "component-solution")
if len(solution_sections) <=2:
#when there are no Hint section
solution_sections[0].find_element(By.TAG_NAME, 'details').click()
solution = solution_sections[0].find_element(By.CLASS_NAME, 'content').text
else:
#when there are Hint section
solution_sections[1].find_element(By.TAG_NAME, 'details').click()
solution = solution_sections[1].find_element(By.CLASS_NAME, 'content').text
#Extract url to access the lab environment
##Find the "Start lab" button and click it
start_button = self.driver.find_element(By.CLASS_NAME, 'button-orange')
start_button.click()
##Get the current tab and switch to the new tab
main_tab = self.driver.current_window_handle
lab_tab = [handle for handle in self.driver.window_handles if handle != main_tab][0]
self.driver.switch_to.window(lab_tab)
##Get the URL of the lab environment
environment_url = self.driver.current_url
##Close the new tab
self.driver.close()
##Switch back to the main tab
self.driver.switch_to.window(main_tab)
lab_info = {
'type': labtype,
'url': lab_url,
'title': title,
'description': "\n".join([p.text for p in paragraphs]),
'solution': solution,
'environment_url': environment_url
}
return lab_info
def check_solved_lab(self,lab_url):
"""
Check if lab was solved.
Args:
lab_url (str): the url of the lab.
Go to the lab url and check if status "Solved or Not Solved".
"""
#Go to lab url
self.driver.get(lab_url)
#get text of status container
lab_status = self.driver.find_element(By.CLASS_NAME, 'lab-status-icon').text
return lab_status