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 protocoloSSHv2
, 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:
Sigla | Nome | Função |
---|---|---|
HTTP | Hypertext Transfer Protocol ou Protocolo de Transferência de Hipertexto | Trata de pedidos e respostas entre o cliente e o servidor na internet. |
FTP | File Transfer Protocol ou Protocolo de Transferência de Arquivos | Transfere documentos hipermídia na internet. |
SMTP | Simple Mail Transfer Protocol ou Protocolo de Transferência de e-mail | Envia e-mail. |
IMAP | Internet Message Access Protocol ou Protocolo de acesso a mensagem da internet | Recebe e-mail. |
Telnet | Telnet | Permite a comunicação remota entre computado-res conectados em rede. |
SSH | Secure Shell ou Terminal Seguro | Permite a comunicação remota entre computado-res conectados em rede, utilizando criptografia |
DHCP | Dynamic 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. |
DNS | Domain 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!