Home Hacking Dojo - Semana \x03
Post
Cancel

Hacking Dojo - Semana \x03

Dando continuidade no desafio do Hacking Dojo, hoje atacaremos a semana 03.

Esta semana é bem densa e uma das que mais rendeu aprendizado até o momento. Basicamente vamos criar alguns utilitários em em Python que provavelmente utilizaremos posteriormente, sejam nos projetos do Dojo, ou no próprio dia-a-dia.

Sem mais delongas, vamos às tasks.

Task \x10

O enunciado desta task pode parecer complexo, mas basicamente o que precisamos é utilizar a biblioteca Paramiko para criarmos dois scripts: um SSH server e um SSH client.

Paramiko é uma biblioteca Python que permite funcionalidades tanto cliente quanto servidor utilizando o protocolo SSHv2, basicamente com ela, podemos utilizar de funcionalidades SSH sem depender do serviço do sistema operacional, inclusive utilizando criptografia.

SSH Client

Partindo do princípio, vamos iniciar o serviço de SSH no Kali.

1
$ sudo systemctl start ssh

Agora precisamos criar um script em Python que seja um cliente para o serviço de SSH em uma VM Linux. Esta VM eu criei com o Debian 11 - Bullseye.

No próprio GitHub do Paramiko é possível encontrar alguns exemplos de scripts que podem ser utilizados como base, ou estudos das funcionalidades. Acontece que o exemplo de client existente, não possui a funcionalidade de executar comandos no server, para isso, vamos precisar de algumas implementações.

A biblioteca Paramiko possui a classe SSHClient() que por sua vez possui a função invoke_shell(). Esta função nos permite criar um canal onde os comandos e respostas serão trasnportados. Abase para o script abaixo, foi do próprio GitHub da biblioteca.

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
#!/usr/bin/env python3

import base64
import getpass
import os
import socket
import sys
import traceback
from paramiko.py3compat import input, u
import paramiko

# windows nao tem termios
try:
    import termios
    import tty

    has_termios = True
except ImportError:
    has_termios = False

# funcao que chama o shell de acordo com o SO
def interactive_shell(chan):
    if has_termios:
        posix_shell(chan)
    else:
        windows_shell(chan)

# funcao para o shell em Linux
def posix_shell(chan):
    import select

    oldtty = termios.tcgetattr(sys.stdin)
    try:
        tty.setraw(sys.stdin.fileno())
        tty.setcbreak(sys.stdin.fileno())
        chan.settimeout(0.0)

        while True:
            r, w, e = select.select([chan, sys.stdin], [], [])
            if chan in r:
                try:
                    x = u(chan.recv(1024))
                    if len(x) == 0:
                        sys.stdout.write("\r\n*** EOF\r\n")
                        break
                    sys.stdout.write(x)
                    sys.stdout.flush()
                except socket.timeout:
                    pass
            if sys.stdin in r:
                x = sys.stdin.read(1)
                if len(x) == 0:
                    break
                chan.send(x)

    finally:
        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)

# funcao para o shell Windows
def windows_shell(chan):
    import threading

    sys.stdout.write(
        "Line-buffered terminal emulation. Press F6 or ^Z to send EOF.\r\n\r\n"
    )

    def writeall(sock):
        while True:
            data = sock.recv(256).decode()
            if not data:
                sys.stdout.write("\r\n*** EOF ***\r\n\r\n")
                sys.stdout.flush()
                break
            sys.stdout.write(data)
            sys.stdout.flush()

    writer = threading.Thread(target=writeall, args=(chan,))
    writer.start()

    try:
        while True:
            d = sys.stdin.read(1)
            if not d:
                break
            chan.send(d)
    except EOFError:
        # user hit ^Z or F6
        pass


# Configuracoes do client
UseGSSAPI = (
    paramiko.GSS_AUTH_AVAILABLE
)  
DoGSSAPIKeyExchange = (
    paramiko.GSS_AUTH_AVAILABLE
)  
port = 22

# get hostname
username = ""
if len(sys.argv) > 1:
    hostname = sys.argv[1]
    if hostname.find("@") >= 0:
        username, hostname = hostname.split("@")
else:
    hostname = input("Hostname: ")
if len(hostname) == 0:
    print("*** Hostname required.")
    sys.exit(1)

if hostname.find(":") >= 0:
    hostname, portstr = hostname.split(":")
    port = int(portstr)


# get username
if username == "":
    default_username = getpass.getuser()
    username = input("Username [%s]: " % default_username)
    if len(username) == 0:
        username = default_username
if not UseGSSAPI and not DoGSSAPIKeyExchange:
    password = getpass.getpass("Password for %s@%s: " % (username, hostname))


# Conectando o cliente com o protocolo SSHv2
try:
    client = paramiko.SSHClient()
    client.load_system_host_keys()
    client.set_missing_host_key_policy(paramiko.WarningPolicy())
    print("*** Conectando...")
    if not UseGSSAPI and not DoGSSAPIKeyExchange:
        client.connect(hostname, port, username, password)
    else:
        try:
            client.connect(
                hostname,
                port,
                username,
                gss_auth=UseGSSAPI,
                gss_kex=DoGSSAPIKeyExchange,
                connect_kwargs={
                    "banner_timeout": 60,
                    },
            )
        except Exception:
            # traceback.print_exc()
            password = getpass.getpass(
                "Password for %s@%s: " % (username, hostname)
            )
            client.connect(hostname, port, username, password)

    chan = client.invoke_shell()
    print(repr(client.get_transport()))
    print("*** Here we go!\n")
    interactive_shell(chan)
    chan.close()
    client.close()

except Exception as e:
    print("*** Caught exception: %s: %s" % (e.__class__, e))
    traceback.print_exc()
    try:
        client.close()
    except:
        pass
    sys.exit(1)

Ao executarmos o script na máquina Debian, ele nos pede o hostname, username e password:

Após as credenciais corretas serem inseridas, temos a conexâo realizada com sucesso.

Temos um client SSH em Python operante em Linux, agora precisamos testá-lo no Windows.

O funcionamento foi o mesmo e a resposta foi o shell.

SSH Server

Agora precisamos criar um SSH server com o paramiko, com isso feito, temos um serviço completo de SSH sem depender de ferramentas do SO.

Antes de qualquer coisa, pensei em um script que fosse compatível com tanto para Linux quanto para Windows, sou seja, 100% baseado em Python, e não no sistema operacional.

No GitHub do Paramiko, também existe um exemplo de SSH server, porém ele exige alguns "apêndices" adicionais, além de que também não tem a funcionalidade de shell. Portanto, partindo deste script como princípio, também precisamos fazer algumas implementações.

Analisando alguns pontos chave do script

Primeiramente, o script precisa de uma chave RSA para rodar, mesmo que a conexão seja feita via password. Para que não fosse preciso um arquivo adicional, criei uma chavhe e a inseri diretamente no script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
chave = '''-----BEGIN RSA PRIVATE KEY-----
MIICWgIBAAKBgQDTj1bqB4WmayWNPB+8jVSYpZYk80Ujvj680pOTh2bORBjbIAyz
oWGW+GUjzKxTiiPvVmxFgx5wdsFvF03v34lEVVhMpouqPAYQ15N37K/ir5XY+9m/
d8ufMCkjeXsQkKqFbAlQcnWMCRnOoPHS3I4vi6hmnDDeeYTSRvfLbW0fhwIBIwKB
gBIiOqZYaoqbeD9OS9z2K9KR2atlTxGxOJPXiP4ESqP3NVScWNwyZ3NXHpyrJLa0
EbVtzsQhLn6rF+TzXnOlcipFvjsem3iYzCpuChfGQ6SovTcOjHV9z+hnpXvQ/fon
soVRZY65wKnF7IAoUwTmJS9opqgrN6kRgCd3DASAMd1bAkEA96SBVWFt/fJBNJ9H
tYnBKZGw0VeHOYmVYbvMSstssn8un+pQpUm9vlG/bp7Oxd/m+b9KWEh2xPfv6zqU
avNwHwJBANqzGZa/EpzF4J8pGti7oIAPUIDGMtfIcmqNXVMckrmzQ2vTfqtkEZsA
4rE1IERRyiJQx6EJsz21wJmGV9WJQ5kCQQDwkS0uXqVdFzgHO6S++tjmjYcxwr3g
H0CoFYSgbddOT6miqRskOQF3DZVkJT3kyuBgU2zKygz52ukQZMqxCb1fAkASvuTv
qfpH87Qq5kQhNKdbbwbmd2NxlNabazPijWuphGTdW0VfJdWfklyS2Kr+iqrs/5wV
HhathJt636Eg7oIjAkA8ht3MQ+XSl9yIJIS8gVpbPxSw5OMfw0PjVE7tBdQruiSc
nvuQES5C9BMHjF39LZiGH1iLQy7FgdHyoP+eodI7
-----END RSA PRIVATE KEY-----'''

Na classe Server que é utilizada para iniciar o servidor, é preciso informar um usuário e senha, que no caso escolhi h41stur:h41stur:

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
class Server(paramiko.ServerInterface):
    def __init__(self):
        self.event = threading.Event()

    def check_channel_request(self, kind, chanid):
        if kind == "session":
            return paramiko.OPEN_SUCCEEDED
        return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED

    def check_auth_password(self, username, password):
        if (username == "h41stur") and (password == "h41stur"):
            return paramiko.AUTH_SUCCESSFUL
        return paramiko.AUTH_FAILED
    
    def get_allowed_auths(self, username):
        return "password"

    def check_channel_shell_request(self, channel):
        self.event.set()
        return True

    def check_channel_pty_request(
        self, channel, term, width, height, pixelwidth, pixelheight, modes
    ):
        return True

A conexão é feita via socket, então as configurações são feitas da forma como já vimos anteriormente:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
try:
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(("", 2200))
except Exception as e:
    print("[-] Erro ao abrir conexao: " + str(e))
    traceback.print_exc()
    sys.exit(1)

try:
    sock.listen(100)
    print("[+] Ouvindo conexoes ...")
    client, addr = sock.accept()
except Exception as e:
    print("[-] Erro de conexao: " + str(e))
    traceback.print_exc()
    sys.exit(1)

print("Conectado!")

Para que os comandos do shell fossem executados de forma normal, precisei criar um loop utilizando a função Popen() da biblioteca subprocess. Também foi preciso criar uma estrutura try, except para transportar o resultado dos comandos, pois em sistemas operacionais no idioma português, o encode UTF-8 não aceita nossos acentos, portanto o except contem o transporte do resultado em LATIN-1.

Neste mesmo loop, inseri um beautify na linha chan.send(os.getcwd() + "> "), para o terminal sempre trazer o path atual, além do comando sair para encerrar a conexão:

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
    while True:
        try:
            chan.send(os.getcwd() + "> ")
            cmd = f.readline().strip("\r\n")
            if cmd == "sair":
                chan.close()
                break
            if cmd[:2] == "cd":
                os.chdir(cmd[3:])
            if len(cmd) > 0:
                proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
                saida = proc.stdout.read() + proc.stderr.read()
                try:
                    saida_str = str(saida, "utf-8").split("\n")
                    for i in saida_str:
                        chan.send("\r\n" + i)
                except Exception as e:
                    saida_str = str(saida, "latin_1").split("\n")
                    for i in saida_str:
                        chan.send(str("\r\n" + i))
        except Exception:
            continue


    chan.close()

O script completo ficou desta forma:

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
import base64
from binascii import hexlify
import os
import socket
import sys
import threading
import traceback
import subprocess
from io import StringIO
import paramiko
from paramiko.py3compat import b, u, decodebytes

chave = '''-----BEGIN RSA PRIVATE KEY-----
MIICWgIBAAKBgQDTj1bqB4WmayWNPB+8jVSYpZYk80Ujvj680pOTh2bORBjbIAyz
oWGW+GUjzKxTiiPvVmxFgx5wdsFvF03v34lEVVhMpouqPAYQ15N37K/ir5XY+9m/
d8ufMCkjeXsQkKqFbAlQcnWMCRnOoPHS3I4vi6hmnDDeeYTSRvfLbW0fhwIBIwKB
gBIiOqZYaoqbeD9OS9z2K9KR2atlTxGxOJPXiP4ESqP3NVScWNwyZ3NXHpyrJLa0
EbVtzsQhLn6rF+TzXnOlcipFvjsem3iYzCpuChfGQ6SovTcOjHV9z+hnpXvQ/fon
soVRZY65wKnF7IAoUwTmJS9opqgrN6kRgCd3DASAMd1bAkEA96SBVWFt/fJBNJ9H
tYnBKZGw0VeHOYmVYbvMSstssn8un+pQpUm9vlG/bp7Oxd/m+b9KWEh2xPfv6zqU
avNwHwJBANqzGZa/EpzF4J8pGti7oIAPUIDGMtfIcmqNXVMckrmzQ2vTfqtkEZsA
4rE1IERRyiJQx6EJsz21wJmGV9WJQ5kCQQDwkS0uXqVdFzgHO6S++tjmjYcxwr3g
H0CoFYSgbddOT6miqRskOQF3DZVkJT3kyuBgU2zKygz52ukQZMqxCb1fAkASvuTv
qfpH87Qq5kQhNKdbbwbmd2NxlNabazPijWuphGTdW0VfJdWfklyS2Kr+iqrs/5wV
HhathJt636Eg7oIjAkA8ht3MQ+XSl9yIJIS8gVpbPxSw5OMfw0PjVE7tBdQruiSc
nvuQES5C9BMHjF39LZiGH1iLQy7FgdHyoP+eodI7
-----END RSA PRIVATE KEY-----'''

chave = StringIO(chave)
host_key = paramiko.RSAKey.from_private_key(chave)

class Server(paramiko.ServerInterface):
    def __init__(self):
        self.event = threading.Event()

    def check_channel_request(self, kind, chanid):
        if kind == "session":
            return paramiko.OPEN_SUCCEEDED
        return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED

    def check_auth_password(self, username, password):
        if (username == "h41stur") and (password == "h41stur"):
            return paramiko.AUTH_SUCCESSFUL
        return paramiko.AUTH_FAILED
    
    def get_allowed_auths(self, username):
        return "password"

    def check_channel_shell_request(self, channel):
        self.event.set()
        return True

    def check_channel_pty_request(
        self, channel, term, width, height, pixelwidth, pixelheight, modes
    ):
        return True


DoGSSAPIKeyExchange = False

# now connect
try:
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(("", 2200))
except Exception as e:
    print("[-] Erro ao abrir conexao: " + str(e))
    traceback.print_exc()
    sys.exit(1)

try:
    sock.listen(100)
    print("[+] Ouvindo conexoes ...")
    client, addr = sock.accept()
except Exception as e:
    print("[-] Erro de conexao: " + str(e))
    traceback.print_exc()
    sys.exit(1)

print("Conectado!")

try:
    t = paramiko.Transport(client, gss_kex=DoGSSAPIKeyExchange)
    t.set_gss_host(socket.getfqdn(""))
    try:
        t.load_server_moduli()
    except:
        print("(Failed to load moduli -- gex will be unsupported.)")
        raise
    t.add_server_key(host_key)
    server = Server()
    try:
        t.start_server(server=server)
    except paramiko.SSHException:
        print("*** SSH negotiation failed.")
        sys.exit(1)

    # wait for auth
    chan = t.accept(20)
    if chan is None:
        print("*** No channel.")
        sys.exit(1)
    print("[+] Autenticado!")

    server.event.wait(10)
    if not server.event.is_set():
        print("*** Client never asked for a shell.")
        sys.exit(1)

    chan.send("\r\n\r\nH41STUR SSH\r\n\r\n")
    
    f = chan.makefile("rU")
    while True:
        try:
            chan.send(os.getcwd() + "> ")
            cmd = f.readline().strip("\r\n")
            if cmd == "sair":
                chan.close()
                break
            if cmd[:2] == "cd":
                os.chdir(cmd[3:])
            if len(cmd) > 0:
                proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
                saida = proc.stdout.read() + proc.stderr.read()
                try:
                    saida_str = str(saida, "utf-8").split("\n")
                    for i in saida_str:
                        chan.send("\r\n" + i)
                except Exception as e:
                    saida_str = str(saida, "latin_1").split("\n")
                    for i in saida_str:
                        chan.send(str("\r\n" + i))
        except Exception:
            continue


    chan.close()

except Exception as e:
    print("*** Caught exception: " + str(e.__class__) + ": " + str(e))
    traceback.print_exc()
    try:
        t.close()
    except:
        pass
    sys.exit(1)

Testando o SSH Server

Ao executar o script na máquina Kali, ele imprime a mensagem informando que está aguardando conexão (mensagem que pode ser retirada para ser mais stealth):

Com a própria ferramenta de SSH na máquina Debian podemos testar a conexão com as credenciais h41stur:h41stur configuradas no script:

Agora também podemos testar o SSH server com nosso script client:

Tudo funcionando perfeitamente, neste momento precisamos testar nosso script client na VM Windows em nosso SSH server no Kali:

Até então, quase tudo pronto nesta task, o que precisamos agora é testar nosso SSH server na VM Windows e conectar via SSH do Kali. Isto se torna muito útil, uma vez que o SSH no Windows é um serviço que precisa ser ativado de alguma forma.

O handler aparentemente está ouvindo conexões normalmente, vamos testar com o client SSH do Kali:

Tudo ocorreu perfeitamente, e conseguimos finalizar a task 16 do Dojo.

Task \x11

Esta task exige um tanto de pesquisa, após algumas leituras para reforçar o conhecimento, fiz um breve resumo sobre o solicitado.

RFC 1918 Address Allocation for Private Internets

De forma geral uma rede privada de internet, é uma rede que utiliza uma determinada quantia de endereços de IP, que são associados entre si em uma rede que não se comunicam com uma rede externa.

Muito utilizado em empresas e até mesmo nos protocolos atuais de internet residencial chamados de redes LAN. Onde cada dispositivo conectado ä rede, não possui um IP público, mas sim um IP privado fornecido por um gateway, mais comummente chamado de roteador. Esta se torna outra razão pela qual o os IPs privados são importantes, pois a quantidade de IPs públicos é limitada na internet. O protocolo IPv6 foi criado para resolver este problema.

Os roteadores são configurados deforma a descartar qualquer tráfego que utilize um IP privado. Este isolamento permite que a rede privada tenha um nível a mais de segurança, pois impede que um dispositivo externo se conecte diretamente a outro dispositivo que esteja na rede interna com IP privado.

Como as conexões não podem ser feitas entre diferentes redes privadas através da internet, vários gateways podem ter a mesma faixa de IP sem que haja conflitos, na verdade na rede residencial é um excelente exemplo, pois a grande maioria dos roteadores fornacem IPs privados iniciados em 192.168.1. ou 192.168.0., sem que sua conexão tenha conflito com a rede de outras residências.

Quando um sidpositivo em uma rete privada precisa se comunicar com a rede externa, o gateway faz esta comunicação, pois ele é o único que possui um IP público utilizando o protocolo NAT (Network Address Translation).

As redes privadas podem ser divididas em classes e faixas que comportam diferentes quantidades de IPs privados, para uma leitura e entendimento mais profundo, recomendo ler esta documentação.

Protocolos e serviços de rede

Os protocolos são instruções desenvolvidas por algorítmos com a finalidade de executar uma tarefa definida. São utilizados entre dois ou mais dispositivos para se comunicarem de alguma forma. Existe uma infinidade de protocolos existentes que podem oferecer outra infinidade de serviços.

Já os serviços são funcionalidades oferecidas numa rede pública ou privada que podem utilizar diversos protocolos. Parece complicado, mas fazermos seu uso o tempo todo, como quando acessamos uma página na internet cujo endereço se inicia com http://, quando o acessamos, estamos fazendo o uso de um serviço de rede que utiliza o protocolo HTTP ou Hypertext Transfer Protocol.

Alguns exemplos protocolos e serviços:

SiglaNomeFunção
HTTPHypertext Transfer Protocol ou
Protocolo de Transferência de Hipertexto
Trata de pedidos e respostas entre o
cliente e o servidor na internet.
FTPFile Transfer Protocol ou
Protocolo de Transferência de Arquivos
Transfere documentos hipermídia na
internet.
SMTPSimple Mail Transfer Protocol ou
Protocolo de Transferência de e-mail
Envia e-mail.
IMAPInternet Message Access Protocol ou
Protocolo de acesso a mensagem da internet
Recebe e-mail.
TelnetTelnetPermite a comunicação remota entre
computado-res conectados em rede.
SSHSecure Shell ou Terminal SeguroPermite a comunicação remota entre
computado-res conectados em rede,
utilizando criptografia
DHCPDynamic Host Configuration Protocol ou
Protocolo de configuração dinâmica de estação
Concede endereços IP e outros
parâmetros dina-micamente para estações
de trabalho.
DNSDomain Name System ou
Sistema de Nome de Domínio
É um sistema de gerenciamento de
nomes hierárquico e distribuído;
permite acessar outro computador na rede
sem ter conhecimento do endereço IP.

Para que estes protocolos e serviços possam se comunicar entre dispositivos, eles utilizarm um modelo que é constítuido de outros dois protocolos, o TCP/IP (Transmission Control Protocol/InternetProtocol).

Para um entendimento um pouco mais profundo sobre o assunto, recomendo ler este artigo.

Task \x12

Esta task tecnicamente é bem simples, precisamos emular em Python, um banner grabbing com a mesma mecânica de quando utilizamos o netcat. Porém, temos que ter em mente, que dependendo do serviço a ser enumerado, precisamos enviar algum comando para que retorne o banner, como na porta 80, ondeprecisamos enviar alguma requisição para que retorne o banner.

Testando no netcat:

Como podemos ver, foi preciso enviar o comando HEAD / para que retornasse o banner.

Com isto em mente, vamos utilizar a biblioteca socket para fazer as requisições nas portas 80, 22, e 3306.

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
import socket

host = "192.168.1.11"
portas = [22, 80, 3306]

for porta in portas:
    if porta == 80:
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.connect((host, porta))
            s.send(b"HEAD /\r\n")
            resp = s.recv(1024)
            s.close()
            print("[+]Banner da porta 80:\n")
            print(resp.decode() + "\r\n")
        except Exception:
            print(Exception)
            continue
    elif porta == 3306:
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.connect((host, porta))
            resp = s.recv(1024)
            s.close()
            print("[+]Banner da porta 3306:\n")
            try:
                print(resp.decode() + "\r\n")
            except:
                print(resp.decode("latin_1" + "\r\n"))
        except Exception:
            continue
    else:
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.connect((host, porta))
            resp = s.recv(1024)
            s.close()
            print("[+]Banner da porta 22:\n")
            print(resp.decode() + "\r\n")
        except Exception:
            continue

Ao executar o script, temos os 3 banners:

O script ainda está bem simplório, porém vamos trabalhá-lo mais nas próximas tasks.

Task \x13

Esta task se torna bem mais simples que a anterior, uma vez que se trata somente de um port scan sem um banner grab. Vamos utilizar a socket novamente, mas ao invés de utilizarmos a função connect() para nos conectarmos nas portas, vamos utilizar a connect_ex().

A diferença entre elas, é que na connect_ex(), caso a porta esteja inacessível, ele retorno uma mensagem de erro, enquanto que na connect(), ele espera pela resposta até atingir o timeout. O script ficou desta forma:

1
2
3
4
5
6
7
8
9
10
11
12
import socket

host = "192.168.1.11"

print("\n[+] BANNER GRABBER\n")

for i in range(65536):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    resp = s.connect_ex((host, i))
    s.close()
    if resp == 0:
        print(f"[{i}] OPEN")

Ao executarmos o script, temos o seguinte retorno:

Isto encerra esta task.

Task \x14

Nesta task vamos criar um ping no host alvo utilizando a biblioteca icmplib do Python. É importante ressaltar que este script só funciona se o ICMP não estiver bloqueado na rede.

O script ficou bem simples:

1
2
3
4
5
6
7
8
9
10
11
from icmplib import ping

host = "192.168.1.11"

p = ping(host, count=1)
resp = p.packets_received

if resp > 0:
    print(f"\n[+] Host {host} vivo!")
else:
    print("\n[-] Host {host} morto!")

Ao executarmos, temos a resposta:

Isto encerra esta task.

Task \x15

A fim de diminuir este artigo, deixo o link para um artigo completo sobre máscara de rede.

Task \x16

Este script é mais informativo, pois serve apenas para consulta. Seguindo o conhecimento adquirido com o artigo citado na task anterior, desenvolvi o seguinte script:

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
import sys

if len(sys.argv) < 2:
    print('''
Modo de uso:
    %s <barramento>
Exemplo:
    %s /9
    ''' %(sys.argv[0], sys.argv[0]))
    sys.exit()

dic = {"/9": 8388606, "/10": 4194302, "/11": 2097150, "/12": 1048574, "/13": 524286, "/14": 262142, "/15": 131070, "/16": 65534, "/17": 32766,
        "/18": 16382, "/19": 8190, "/20": 4094, "/21": 2046, "/22": 1022, "/23": 510, "/24": 254, "/25": 126, "/26": 62, "/27": 30, "/28": 14,
        "/29": 6, "/30": 2, "/31": "2 (*)"}

consulta = sys.argv[1]

info = '''
Bits emprestados        Máscara         Prefixo     Subredes (2n)       Hosts (2n-2)
1                       255.128.0.0         /9          2               8388606
2                       255.192.0.0         /10         4               4194302
3                       255.224.0.0         /11         8               2097150
4                       255.240.0.0         /12         16              1048574
5                       255.248.0.0         /13         32              524286
6                       255.252.0.0         /14         64              262142
7                       255.254.0.0         /15         128             131070
8                       255.255.0.0         /16         256             65534
9                       255.255.128.0       /17         512             32766
10                      255.255.192.0       /18         1024            16382
11                      255.255.224.0       /19         2048            8190
12                      255.255.240.0       /20         4096            4094
13                      255.255.248.0       /21         8192            2046
14                      255.255.252.0       /22         16384           1022
15                      255.255.254.0       /23         32768           510
16                      255.255.255.0       /24         65536           254
17                      255.255.255.128     /25         131072          126
18                      255.255.255.192     /26         262144          62
19                      255.255.255.224     /27         524288          30
20                      255.255.255.240     /28         1048576         14
21                      255.255.255.248     /29         2097152         6
22                      255.255.255.252     /30         4194304         2
23                      255.255.255.254     /31         8388608         2 (*)
'''

print(f"\n[+] No barramento {consulta} cabem {dic[consulta]} hosts.")
print("\nINFORMATIVO:\n", info)

Ao executá-lo com um barramento específico como argumento, ele retorna a seguinte resposta:

Isso encerra esta task.

Task \x17

De forma bem resumida:

Função

Uma função é basicamente um trecho de código que pode ser chamado pelo seu nome. Pode receber argumentos ou parâmetros e podem retornar dados como resposta de sua execução.

Método

Um método é um trecho de código que também pode ser chamado pelo seu nome associado a um objeto. Muito parecido com uma função, porém com algumas diferenças:

  • Um método é passado implicitamente no objeto no qual foi chamado.
  • É capaz de operar com dados existentes na classe.

Classe

A classe é um conjunto de objetos distintos, porém com as mesmas características e comportamentos. Assim como descrito na semana \x01 do Dojo, um exemplo de classe abstrato ao mundo real seria uma classe Motocicleta com as propriedades cor, modelo, cilindradas, torque e os métodos acelerar e frear e a classe Carro, tem as propriedades cor, modelo, cilindradas, torque e os métodos acelerar e frear.

Pode-se descobrir a cor da moto perguntando por "Motocicleta.cor", sendo que da mesma forma podemos saber a cor do carro perguntando "Carro.cor". Neste exemplo os dois atributos tem o mesmo nome “cor”, mas cada um tem sua própria classe náo tendo conflito entre si.

Task \x18

Esta task é uma evolução do script feito na task 20, a diferença é que ao invés de passarmos o IP alvo como parâmetro, vamos ler uma lista com IPs para pingar.

Minha lista com 10 IPs:

1
2
3
4
5
6
7
8
9
10
11
$ cat hosts.txt 
192.168.1.2
192.168.1.3
192.168.1.4
192.168.1.5
192.168.1.6
192.168.1.7
192.168.1.8
192.168.1.9
192.168.1.10
192.168.1.11

E o script continuou simples:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from icmplib import ping

host = "192.168.0.189"

def check(ip):
    p = ping(ip, count=1)
    resp = p.packets_received
    if resp > 0:
        print("[+] Host vivo:", ip)

with open("hosts.txt", "r") as h:
    for i in h:
        i = i.strip()
        check(i)

Ao executarmos o script, temos o seguinte retorno:

Task \x18 (de novo)

Temos duas tasks 24, talvez algum erro de digitação (ou proposital??), então vamos atacá-la.

Antes de começar, precisamos entender que o SO possui várias interfaces de trabalho, e antes de conseguirmos obter o IP e a máscara de rede, precisamos saber qual a interface correta a ser analisada.

Nesta task, tive que pesquisar alguma biblioteca ou função que conseguisse me retornar as informações que preciso. Acabei me deparando com a biblioteca netifaces.

Esta lib consegue listar todas as interfaces do dispositivo, e extrair algumas informações. Porém ainda precisava encontrar o IP correspondente à interface correta.

Para isso, utilizei a biblioteca socket para fazer uma requisição no IP do Google: 8.8.8.8, em seguida utilizei a função getsockname() para obter a resposta da requisição.

Esta função retorna o socket (IP:porta) utilizados na requisição, pegando o primeiro elemento da função, temos o IP para comparar com as informaçoes obtidas com netifaces.

O script ficou desta forma:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import netifaces
import socket

def self_ip():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect(("8.8.8.8", 80))
    ip = s.getsockname()[0]
    s.close()
    return ip

for i in netifaces.interfaces():
    info = netifaces.ifaddresses(i)
    try:
        rede = info[2][0]
        addr = rede["addr"]
        mask = rede["netmask"]
        ip = self_ip()
        if addr == ip:
            print("\n[+] A interface e:",i)
            print("\n[+] IP:", addr)
            print("[+] Netmask:", mask + "\n")
            print("*" * 50 + "\n")
    except Exception:
        continue

Ao executar o script na VM Windows, temos o seguinte retorno:

Isso encerra esta task.

Task \x19

Esta task é a mais complexa desta semana do Dojo, precisamos montar uma ferramenta de discover de rede.

Basicamente, vamos utilizar o script anterior para encontrar o IP e a máscara de rede, com estes dados, precisamos calcular todo o range de IP desta subrede, para isto utilizei a função IPNetwork() da biblioteca netaddr.

Esta função recebe o IP + a máscara de subrede (Ex.: 192.168.1.7/24) e calcula todo o range possível de IPs dentro desta subrede. A partir daí, vasculha todos os hosts ativos dentro da rede privada, quando encontra um host vivo, começa o scan de portas conforme já fizemos anteriormente.

Neste script, utilizamos basicamente tudo que foi aprendido nesta semana em uma única ferramente, além de adicionar algo mais, como a classe com as cores para o terminal, atendendo os requisitos solicitados.

O script ficou desta forma:

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
from ipaddress import ip_address
from netaddr import *
from icmplib import ping
import netifaces
import socket

lista = []

class cor:
        normal = '\033[0m'
        azul = '\033[94m'
        verm = '\033[91m'
        verde = '\033[92m'

dic_msk = {
        "255.128.0.0": "/9",
        "255.192.0.0": "/10",
        "255.224.0.0": "/11",
        "255.240.0.0": "/12",
        "255.248.0.0": "/13",
        "255.252.0.0": "/14",
        "255.254.0.0": "/15",
        "255.255.0.0": "/16",
        "255.255.128.0": "/17",
        "255.255.192.0": "/18",
        "255.255.224.0": "/19",
        "255.255.240.0": "/20",
        "255.255.248.0": "/21",
        "255.255.252.0": "/22",
        "255.255.254.0": "/23",
        "255.255.255.0": "/24",
        "255.255.255.128": "/25",
        "255.255.255.192": "/26",
        "255.255.255.224": "/27",
        "255.255.255.240": "/28",
        "255.255.255.248": "/29",
        "255.255.255.252": "/30",
        "255.255.255.254": "/31"
        }

def port_scan(host):
    for i in range(65536):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        resp = s.connect_ex((host, i))
        s.close()
        if resp == 0:
            print(cor.verde + str(i) + cor.verm + " --- " + cor.azul + "ABERTA" + cor.normal)

def icmp(host):
    p = ping(host, count=1)
    resp = p.packets_received
    if resp > 0:
        print("\n[" + cor.verde + "-" + cor.normal + "] " + host + "\n")
        port_scan(host)

def self_ip():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect(("8.8.8.8", 80))
    resp = s.getsockname()[0]
    s.close()
    return resp

def mask():
    for i in netifaces.interfaces():
        info = netifaces.ifaddresses(i)
        try:
            rede = info[2][0]
            addr = rede["addr"]
            msk = rede["netmask"]
            host = self_ip()
            mask = dic_msk[msk]
            if addr == host:
                host += mask
                return host
        except Exception:
            continue

def hosts():
    sub = mask()
    print("\nRede:", sub + "\n")
    sub = IPNetwork(sub)
    for i in sub:
        i = str(i)
        icmp(i)

if __name__ == '__main__':
    hosts()

A execução deste script pode demorar de acordo com a quantidade de hosts vivos na rede, sua resposta fica desta forma:

Este script ainda pode ser melhorado com a implementação do top ports e/ou portas específicas visando a melhor performance e acertabilidade.

Desta forma finalizamos as tasks da semana 03, em breve continuaremos na trilha.

Bons estudos!

This post is licensed under CC BY 4.0 by the author.