-
Notifications
You must be signed in to change notification settings - Fork 0
/
core.py
187 lines (149 loc) · 7.38 KB
/
core.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
import socket
import requests
import re
import random
import time
from bs4 import BeautifulSoup
from whois import whois
# Global values
IMPORTANTENTRIES = "common.txt" # Wordlist for something that should be checked to identify server
IMPORTANTHEADERS = "OWASP_dangerousHeaders.txt" # Wordlist for headers that should be checked
USERAGENTS = "userAgents.txt" # List of random user-agents
MAXREQWAIT = 3 # Max wait time between requests
DEFAULTPROTOCOL = "http" # Default protocol
DEFAULTPORT = 80 # Default port
# Convert target URL to IP address
def ConvertToIP(url):
try:
# Check if the address starts with 'http://' or 'https://'
match = re.match(r'(https?://)?(.*?)(/)?$', url)
protocolPrefix, domain, _ = match.groups()
# Resolve to IPv4 address
IP = socket.gethostbyname(domain)
return IP
except (socket.gaierror, TimeoutError):
return None
# Generate random string
def RandomString():
res = ""
chars = "1234567890qwertyuiopasdfghjklzxcvbnm"
for i in range(random.randint(5, 15)):
res += random.choice(chars)
return res
# Load wordlist by name
def LoadList(name):
with open(f"wordlists/{name}", 'r') as file:
return file.read().split('\n')
class Target:
def __init__(self, hostname=None, ip=None, protocol=DEFAULTPROTOCOL, port=None, path="", timeout=0):
self.hostname = hostname
self.IP = ip
self.protocol = protocol
self.port = port
self.path = path
self.timeout = timeout
def GetFullURL(self):
if self.port: port = self.port
else: port = 443 if self.protocol == "https" else 80
return f"{self.protocol}://{self.hostname}:{port}/{self.path}"
def Parse(self, target):
if target.startswith("https"): self.protocol = "https"
elif target.startswith("http"): self.protocol = "http"
else: self.protocol = DEFAULTPROTOCOL
target = target.replace("https://", '').replace("http://", '')
# target = target.replace("//", '/').replace("::", ':')
buff = target.split('/', 1)[0]
if ":" in buff:
self.port = int(buff.split(':')[1])
self.hostname = buff.split(':')[0]
else:
self.port = None # DEFAULTPORT
self.hostname = buff
buff = target.split('/', 1)
if len(buff) > 1: self.path = buff[1]
# if self.path and self.path[-1] != '/': self.path += '/'
def Setup(self):
self.IP = ConvertToIP(self.hostname)
if not self.IP: raise RuntimeError("Host doesn't seem to be reachable")
# requests.head(self.GetFullURL())
class Core:
def __init__(self, target="127.0.0.1") -> None:
self.version = "0.8"
self.userAgent = "webzir/" + self.version
self.target = Target()
self.results = {}
self.wordlist = []
self.wayback = []
self.debug = False
self.allowRedirect = False
def SetTarget(self, t):
self.target.Parse(t)
self.target.Setup()
def RandomizeUserAgent(self):
self.userAgent = random.choice(LoadList(USERAGENTS))
def Setup(self, randomUserAgent=False, verbose=False, allowRedirect=True):
if randomUserAgent: self.RandomizeUserAgent()
self.debug = verbose
self.allowRedirect = allowRedirect
def DetectTech(self):
if self.debug: print(f"[v] Getting server headers...")
response = requests.head(self.target.GetFullURL(), headers={"User-Agent": self.userAgent})
for header in response.headers:
if header in LoadList(IMPORTANTHEADERS):
self.results[header] = response.headers[header] # WARNING: dangerous, might be overwritten
response = requests.head(self.target.GetFullURL(), headers={"User-Agent": self.userAgent}, allow_redirects=True)
for header in response.headers:
if header in LoadList(IMPORTANTHEADERS):
self.results[header] = response.headers[header] # WARNING: dangerous, might be overwritten
if self.debug: print(f"[v] Server headers received")
if self.debug: print(f"[v] Checking for availability of bruteforce enumeration...")
testReqURL = f"{self.target.GetFullURL()}{RandomString()}"
nonExistentResponse = requests.head(f"{testReqURL}", headers={"User-Agent": self.userAgent}, allow_redirects=True)
if nonExistentResponse.status_code == 429:
self.target.timeout = min(MAXREQWAIT, int(nonExistentResponse.headers["Retry-after"])/1000 + 1)
if self.debug: print(f"[v] Set up Retry-after ({self.target.timeout})")
elif nonExistentResponse.status_code != 404:
raise RuntimeError(f"Response for non-existent URL {testReqURL} responded with {nonExistentResponse}")
if nonExistentResponse.status_code in [429, 404]:
if self.debug: print(f"[v] Starting bruteforce...")
for variant in LoadList(IMPORTANTENTRIES):
req = requests.head(f"{self.target.GetFullURL()}{variant}", headers={"User-Agent": self.userAgent}, allow_redirects=self.allowRedirect)
if req.status_code != 404:
if not "Interesting findings" in self.results: self.results["Interesting findings"] = []
self.results["Interesting findings"] += [f"{variant} ({req.status_code})"]
if self.debug: print(f"[v] Found {variant} ({req.status_code})")
time.sleep(self.target.timeout)
def ScrapeWordlist(self):
req = requests.get(self.target.GetFullURL(), headers={"User-Agent": self.userAgent}, allow_redirects=True)
soup = BeautifulSoup(req.content, "html.parser")
text = soup.find_all(text=True)
text = ' '.join(text)
text = text.replace('\n', ' ')
while " " in text: text = text.replace(" ", ' ') # Perhaps remove spaces entirely?
self.wordlist = list(dict.fromkeys([word for word in text.split(' ') if word and len(word) < 10 and len(word) > 1]))
def Wayback(self):
if self.debug: print(f"[v] Searching in Wayback machine...")
portal = f"http://web.archive.org/cdx/search/cdx?url=*.{self.target.hostname}/*&output=json&fl=original&collapse=urlkey"
req = requests.get(portal, headers={"User-Agent": self.userAgent})
result = []
for v in req.json()[1:]:
if type(v) == list:
result += v
else: result += [v]
result = list(dict.fromkeys(result))
if result: self.wayback = result
def Whois(self):
if self.target.IP == self.target.hostname: return
# TODO: add try...except
req = whois(self.target.hostname)
res = {}
if req.registrar: res["Registrar"] = req.registrar
if req.registrant: res["Registrant"] = req.registrant
if req.creation_date: res["Creation date"] = req.creation_date[0] if type(req.creation_date) == list else req.creation_date
if req.updated_date: res["Updated date"] = req.updated_date
if req.org: res["Organisation"] = req.org
if req.address: res["Address"] = req.address
if req.dnssec: res["DNSSEC"] = req.dnssec
if req.registrant_postal_code: res["Registrant postal code"] = req.registrant_postal_code
if req.country: res["Country"] = req.country
if res: self.results["Whois"] = res