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 5

    Criando um PortScan

    Podemos também desenvolver nosso próprio PortScan, semelhante ao Nmap. Veja um exemplo simples:

    
    import socket
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    print(sock.connect_ex(("globo.com", 80))) # O connect_ex retorna um código
    
    

    Se a porta estiver aberta, ele retornará 0, se estiver fechada ele dará um erro, que será tratado posteriormente, podemos fazer assim, definindo um timeout pra ele desconectar:

    
    import socket
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    sock.settimeout(0.1)
    
    print(sock.connect_ex(("globo.com", 80))) # O connect_ex retorna um código
    
    

    E usando um if:

    
    import socket
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    sock.settimeout(0.1)
    
    porta = 80
    
    if sock.connect_ex(("globo.com", porta)) == 0:
        print(f"{porta}/tcp aberta!")
    
    

    Pra ele ficar funcional, deixe ele assim:

    
    import socket
    
    def checkPort(host, porta):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
        sock.settimeout(0.1)
    
        verif = sock.connect_ex((host, porta))
    
        if verif == 0:
            print(f"{porta}/tcp de {host} aberta!")
    
        sock.close()
    
    host = str(input("Insira o domínio ou IP: "))
    
    for p in range(65536):
        checkPort(host, p)
    
    

    Mas o programa assim ficará muito lento, podemos colocar menos portas no range, como 1025 (pra ele contar do 0 ao 1024).

    Para resolver isso, define essa função e comente o for por enquanto:

    
    def getIP(host):
        sock = socket.getaddrinfo(host, None, socket.AF_INET)
        print(sock[0][4][0])
    
    host = str(input("Insira o domínio ou IP: "))
    
    getIP(host)
    
    

    Daí, apenas troque o print da função pelo return, e deixe a chamada assim:

    
    def getIP(host):
        sock = socket.getaddrinfo(host, None, socket.AF_INET)
        return sock[0][4][0]
    
    host = str(input("Insira o domínio ou IP: "))
    addr = getIP(host)
    
    for p in range(65536):
        checkPort(addr, p)
    
    

    Escaneando as 1000 Primeiras Portas

    Entendendo o arquivo nmap-service, do programa Nmap (encontrado em C:\Program Files (x86)\Nmap no Windows ou /usr/share/nmap no Linux), nós vemos as portas mais comuns e a frequência com as quais elas são encontradas. Veja um trecho de algumas linhas:

    
    http    80/sctp 0.000000    # www-http | www | World Wide Web HTTP
    http    80/tcp  0.484143    # World Wide Web HTTP
    http    80/udp  0.035767    # World Wide Web HTTP
    
    

    O número decimal ao lado da porta representa a frequência relativa com que determinado serviço foi observado naquela porta e protocolo, de 0 a 1. Por exemplo, o número 0.484143 indica que, em 48,4143% dos casos observados, o serviço HTTP foi encontrado na porta 80 usando o protocolo TCP.

    No Linux, para filtrar apenas as portas mais usadas (que são as com os números decimais maiores, fazemos assim:

    
    cat /usr/share/nmap/nmap-services | grep -v "^#" | grep "tcp" | sort -k3 -r | head -n 1000
    
    

    Mas pra facilitar, principalmente se não estiver no Linux, podemos baixar esse arquivo pra teste clicando aqui!

    Num arquivo de teste, coloque esse código pra ler o arquivo TXT criado:

    
    with open("1000-portas-nmap.txt") as arqPort:
        for p in arqPort.readlines():
            linha = p.replace("\n", "")
            print(linha.encode())
    
    

    Com a filtragem splitada:

    
    with open("1000-portas-nmap.txt") as arqPort:
        for p in arqPort.readlines():
            linha = p.replace("\n", "")
            porta = linha.split("\t")[1].split("/")[0]
            servico = linha.split("\t")[0]
            print(f"{porta}/{servico}")
    
    

    E pra ficar tipo um dicionário ou JSON, faça assim:

    
    print(f"{porta}: \"{servico}\", ", end = "") # Não esqueça das aspas e da vírgula
    
    

    Daí, execute o código e faça um dicionário com elas no PortScan, assim:

    
    host = str(input("Insira o domínio ou IP: "))
    addr = getIP(host)
    
    dicPortas = {80: "http", 23: "telnet", 443: "https", 21: "ftp", 22: "ssh", 25: "smtp"} # Esse é só um exemplo, ele teria mil índices.
    
    for p in dicPortas:
        checkPort(addr, p)
    
    

    Agora, coloque antes de todas as funções, o mesmo dicionário com as portas e o indicador global, assim:

    
    global dicPortas
    dicPortas = {80: "http", 23: "telnet", 443: "https", 21: "ftp", 22: "ssh", 25: "smtp"}
    
    

    E daí, altere apenas o print dentro do if de checkPort, assim:

    
    if verif == 0:
        op = f"{porta}/tcp"
        print(f"{op:<15} {dicPortas[porta]:<15} {host:<18} ABERTA")
    
    

    E acima do for que invoca a função, colocamos apenas isso:

    
    print(f"{'PORTA':<15} {'SERVIÇO':<15} {'IP':<18} {'SITUAÇÃO'}")
    
    

    Assim ele formatará de uma forma mais agradável.

    Criando PortScan mais Rápido que Nmap

    Para fazer o portscan mais rápido, precisaremos usar threads. Um thread permite que duas ou mais funções sejam executadas praticamente ao mesmo tempo. Num arquivo de teste, podemos fazer um exemplo.

    Exemplo sem thread:

    
    def imprimir(texto):
        for i in range(5):
            print(f"{texto} - {i}")
    
    imprimir("f1")
    imprimir("f2")
    
    

    Exemplo com thread:

    
    import threading
    import time
    
    def imprimir(texto):
        for i in range(5):
            time.sleep(1) # Só pra retardar o script para vermos a execução, normalmente não usamos time
            print(f"{texto} - {i}")
    
    t1 = threading.Thread(target = imprimir, args = ("f1",)) # Não pode esquecer da vírgula depois dos argumentos
    t2 = threading.Thread(target = imprimir, args = ("f2",))
    
    t1.start()
    t2.start()
    
    

    No arquivo PortScan.py, coloque a importação de threading acima, e dentro do for, faça assim:

    
    for p in dicPortas:
        t = threading.Thread(target = checkPort, args = (addr, p,))
        t.start()
    
    

    Dessa forma, ele, antes de finalizar a análise de uma porta, ele vai executar a seguinte análise, e deixar o código mais rápido.

    Criando um Dirb

    Para fazer um programa tipo Dirb, instale a biblioteca requests. Baixe também uma wordlist, podemos baixar um exemplo clicando aqui.

    Primeiramente, faça esse código aqui:

    
    dominio = str(input("Insira o domínio: "))
    wordlist = "wordlist-diretorios-e-arquivos.txt"
    
    if dominio[-1] != "/":
        dominio += "/"
    
    print(dominio)
    
    with open(wordlist) as arqWord:
        for w in arqWord.readlines():
            item = w.replace("\n", "")
            url = f"{dominio}{item}"
            print(url)
    
    

    Em um servidor, quando a gente digita o diretório de um site sem a barra no final (/), ele retorna no request a informação 301 (Moved Permanented, que indica redirecionamento), com isso podemos saber o que é pasta e o que é arquivo (já que na wordlist tem os dois misturados), e o location no request retorna o link correto. Isso é usando o GET, mas este consome muitos recursos do servidor, por isso é recomendado usar o Header.

    Esse é um exemplo de um trecho de um header:

    
    HTTP/1.1 301 Moved Permanently
    Date: Thu, 16 Oct 2025 11:47:56 GMT
    Content-Type: text/html; charset=iso-8859-1
    Transfer-Encoding: chunked
    Connection: keep-alive
    Server: cloudflare
    Location: http://www.bancocn.com/images/
    
    

    Pra iniciar, deixe o script assim:

    
    import requests
    
    dominio = str(input("Insira o domínio: "))
    wordlist = "wordlist-diretorios-e-arquivos.txt"
    
    if dominio[-1] != "/":
        dominio += "/"
    
    with open(wordlist) as arqWord:
        for w in arqWord.readlines():
            item = w.replace("\n", "")
            url = f"{dominio}{item}"
    
            re = requests.head(url, allow_redirects = False)
    
            print(f"{url} - {re.status_code}")    
    
    

    Daí, pros não encontrados ele retornará 404, pros diretórios encontrados retornará 301 ou 302.

    Pra continuar, retire o último print e coloque isso no lugar dele:

    
    code = re.status_code
    
    if code in (301, 302):
        local = re.headers.get("Location")
    
        if url + "/" == local:
            print(f"Pasta: {url}/ - CODE: {code}")
    elif code in (200, 403):
        print(f"Arquivo: {url} - CODE: {code}")
    
    

    E depois, reescreva o programa com funções, assim:

    
    import requests
    
    def check(url):
        re = requests.head(url, allow_redirects = False)
        code = re.status_code
    
        if code in (301, 302):
            local = re.headers.get("Location")
            
            if url + "/" == local:
                print(f"Pasta: {url}/ - CODE: {code}")
        elif code in (200, 403):
            print(f"Arquivo: {url} - CODE: {code}")
    
    def looping(wordl, dom):
        for w in wordl:
            item = w.replace("\n", "")
            url = f"{dom}{item}"
            check(url)
    
    dominio = str(input("Insira o domínio: "))
    wordlist = "wordlist-diretorios-e-arquivos.txt"
    
    if dominio[-1] != "/":
        dominio += "/"
    
    with open(wordlist) as arqWord:
        word = arqWord.readlines()
    
    looping(word, dominio)
    
    

    No começo do código, antes de quaisquer funções, coloque isso:

    
    global pastas
    pastas = list()
    
    

    E dentro do segundo if da função check, apenas coloque o append, assim:

    
    if url + "/" == local:
        print(f"Pasta: {url}/ - CODE: {code}")
        pastas.append(url)
    
    

    No final de todo o código, abaixo da invocação da função looping, podemos rodar o print com as pastas pra ver se elas estão sendo capturadas pelo script:

    
    print(pastas)
    
    

    E no lugar do print acima, coloque isso:

    
    if len(pastas) >= 1: # Verifica se existe pelo menos um arquivo na pasta
        for p in pastas:
            print(p)
    
    

    E pra ele poder analisar recursivamente as pastas, faça assim:

    
    if len(pastas) >= 1: # Verifica se existe pelo menos um arquivo na pasta
        for p in pastas:
            dominio = p + "/"
            looping(word, dominio)
    
    

    Só para entendimento, esses são os códigos usados nesse script:

    Código Significado Curto
    200 Ok
    301 Movido Permanentemente
    302 Movido Temporariamente
    403 Proibido
    404 Não Encontrado

    PS: O proibido dá a entender que o arquivo existe no servidor, mas foi bloqueado pelo firewall ou outra ferramenta de segurança do mesmo.