Skip to content

feat: v3 release -- complete rewrite of xsrfprobe with better engine #65

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion xsrfprobe/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe

__version__ = "4.4.29"
__version__ = "3.0.0"
__license__ = "GNU General Public License v3 (GPLv3)"
211 changes: 9 additions & 202 deletions xsrfprobe/core/banner.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,212 +10,19 @@
# https://github.com/0xInfection/XSRF-Probe

# Just for some fancy benner to appear at beginning

import time
from xsrfprobe import __version__
import xsrfprobe.core.colors

colors = xsrfprobe.core.colors.color()

SLEEP_TIME = 0


def banner():
"""Display the program banner"""
print("\n\n")
time.sleep(SLEEP_TIME)
print(
colors.ORANGE
+ " _____ _____ _____ _____ _____ "
)
time.sleep(SLEEP_TIME)
print(
colors.RED
+ " __"
+ colors.ORANGE
+ "|"
+ colors.RED
+ "__ "
+ colors.ORANGE
+ " |_ "
+ colors.RED
+ "__"
+ colors.ORANGE
+ "|"
+ colors.RED
+ "___ "
+ colors.ORANGE
+ " |_ "
+ colors.RED
+ "__"
+ colors.ORANGE
+ "|"
+ colors.RED
+ "___ "
+ colors.ORANGE
+ "|_ "
+ colors.RED
+ "_"
+ colors.ORANGE
+ "|"
+ colors.RED
+ "____ "
+ colors.ORANGE
+ "|_"
+ colors.RED
+ " _"
+ colors.ORANGE
+ "|"
+ colors.RED
+ "____ "
+ colors.ORANGE
+ "|_ "
+ colors.RED
+ " _____ _____ ______ ______ "
)
time.sleep(SLEEP_TIME)
print(
colors.RED
+ " \ ` / "
+ colors.ORANGE
+ "|"
+ colors.RED
+ "| ___| "
+ colors.ORANGE
+ "|"
+ colors.RED
+ "| _ _| "
+ colors.ORANGE
+ "|"
+ colors.RED
+ "| ___| "
+ colors.ORANGE
+ "| "
+ colors.RED
+ "| _ | "
+ colors.ORANGE
+ "|"
+ colors.RED
+ "| _ ,' / \| _ )| ___| "
)
time.sleep(SLEEP_TIME)
print(
colors.RED
+ " > < "
+ colors.ORANGE
+ "|"
+ colors.RED
+ " `-.`-. "
+ colors.ORANGE
+ "|"
+ colors.RED
+ "| \ "
+ colors.ORANGE
+ "|"
+ colors.RED
+ "| ___| "
+ colors.ORANGE
+ "|"
+ colors.RED
+ " | __| "
+ colors.ORANGE
+ "|"
+ colors.RED
+ "| \ | - || |_ { | ___| "
)
time.sleep(SLEEP_TIME)
print(
colors.RED
+ " /__/__\ "
+ colors.ORANGE
+ "_|"
+ colors.RED
+ "|______| "
+ colors.ORANGE
+ "_|"
+ colors.RED
+ "|__|\__\ "
+ colors.ORANGE
+ " _|"
+ colors.RED
+ "|___| "
+ colors.ORANGE
+ " _|"
+ colors.RED
+ " |___| "
+ colors.ORANGE
+ " _|"
+ colors.RED
+ "|__|\__\\\_____/|______)|______| "
)
time.sleep(SLEEP_TIME)
print(
colors.ORANGE
+ " |_____| |_____| |_____| |_____| |_____| \n\n"
)
time.sleep(SLEEP_TIME)
print(r'''
________ ______
____ __________________ __/_______________________ /______
__ |/_/_ ___/_ ___/_ /_ ___ __ \_ ___/ __ \_ __ \ _ \
__> < _(__ )_ / _ __/ __ /_/ / / / /_/ / /_/ / __/
/_/|_| /____/ /_/ /_/ _ .___//_/ \____//_.___/\___/
/_/

~ 0xInfection | %s

def banabout(): # some fancy banner stuff :p
print(
colors.BLUE
+ " [---] "
+ colors.GREY
+ "XSRFProbe,"
+ colors.RED
+ " A"
+ colors.ORANGE
+ " Cross Site Request Forgery "
+ colors.RED
+ "Audit Toolkit "
+ colors.BLUE
+ "[---]"
)
time.sleep(SLEEP_TIME)
print(
colors.BLUE
+ " [---] [---]"
)
time.sleep(SLEEP_TIME)
print(
colors.BLUE
+ " [---] "
+ colors.PURPLE
+ " "
+ colors.GREEN
+ "~ Author : "
+ colors.CYAN
+ "Pinaki Mondal ~ "
+ colors.BLUE
+ " [---]"
)
time.sleep(SLEEP_TIME)
print(
colors.BLUE
+ " [---] "
+ colors.CYAN
+ " ~ github.com / "
+ colors.GREY
+ "0xInfection ~ "
+ colors.BLUE
+ " [---]"
)
time.sleep(SLEEP_TIME)
print(
colors.BLUE
+ " [---] [---]"
)
time.sleep(SLEEP_TIME)
print(
colors.BLUE
+ " [---] "
+ colors.ORANGE
+ " ~ Version "
+ colors.RED
+ __version__
+ colors.ORANGE
+ " ~ "
+ colors.BLUE
+ " [---]\n"
)
time.sleep(SLEEP_TIME)
''' % __version__)
53 changes: 0 additions & 53 deletions xsrfprobe/core/colors.py

This file was deleted.

95 changes: 95 additions & 0 deletions xsrfprobe/core/diff.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#!/usr/bin/env python3
# coding: utf-8

# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
# -:-:-:-:-:-:-::-:-:#

# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe

import re
import logging
from bs4 import BeautifulSoup
from difflib import SequenceMatcher
from core.schema import BenchmarkResult
from files.config import SIMILARITY_THRESHOLD

class DiffEngine:
def __init__(self):
self.cleaner_regex = re.compile(r"\b[\da-fA-F]{8,}\b|\d+") # Regex to remove dynamic parts
self.logger = logging.getLogger("DiffEngine")

def getCleanedResponse(self, html: str) -> list[str]:
"""
Parse the HTML and extract cleaned text content without dynamic parts.
"""
soup = BeautifulSoup(html, "html.parser")
for tag in soup(["script", "style"]):
tag.decompose()
text = soup.get_text(separator=" ", strip=True)
cleaned_text = self.cleaner_regex.sub("", text)
return cleaned_text.split()

def diffHeaders(self, headers: tuple[dict, dict]) -> tuple[set, set, dict, set]:
"""
Compare two sets of headers and return the common headers.
"""
headersx, headersy = headers
keys1 = set(headersx.keys())
keys2 = set(headersy.keys())
added_headers = keys2 - keys1
removed_headers = keys1 - keys2
common_headers = keys1 & keys2

changed_headers = {key: (headersx[key], headersy[key]) for key in common_headers if headersx[key] != headersy[key]}
return added_headers, removed_headers, changed_headers, common_headers

def prepareBenchmarkResponse(self, response_bodies: tuple[str, str], statuses: tuple[int, int], headers: tuple) -> BenchmarkResult:
"""
Compare two HTML responses and return common static parts.
"""
responsex, responsey = response_bodies
content1 = self.getCleanedResponse(responsex)
content2 = self.getCleanedResponse(responsey)
matcher = SequenceMatcher(None, content1, content2)
common_parts = [content1[block.a:block.a + block.size] for block in matcher.get_matching_blocks() if block.size > 0]
# we expect both the status codes of the base benchmark to be the same
statusx, statusy = statuses
if statusx != statusy:
self.logger.warning("Status codes of the base benchmark responses are different. This may lead to inaccurate results.")

headersx, headersy = headers
diffed_headers = self.diffHeaders((headersx, headersy))
added_headers, removed_headers, changed_headers, common_headers = diffed_headers

return BenchmarkResult(
base_benchmark=[item for sublist in common_parts for item in sublist],
status_code=statusx if statusx == statusy else 0,
headers={key: headersx[key] for key in common_headers},
)

def performBenchmark(self, base_benchmark: BenchmarkResult, new_html: str) -> float:
"""
Calculate how much of the common parts match the new HTML response.
"""
new_content = self.getCleanedResponse(new_html)
matcher = SequenceMatcher(None, base_benchmark.base_benchmark, new_content)
return matcher.ratio() * 100

def benchmarkPassed(self, base_benchmark: BenchmarkResult, response_to_benchmark: str, status: int) -> bool:
"""
Perform the benchmark and check if the new HTML response matches the threshold.
"""
if base_benchmark.status_code != status:
if base_benchmark.status_code == 0:
pass
else:
return False

match_ratio = self.performBenchmark(base_benchmark, response_to_benchmark)
if match_ratio >= SIMILARITY_THRESHOLD:
return True

return False
Loading