1. Cisco 무선 LAN 컨트롤러

- Access Point (AP)를 중앙에서 관리 및 제어할 수 있도록 해줌 [1]

2. 취약점

[사진 1] CVE-2025-20188 [2]

- Cisco 무선 LAN 컨트롤러의 Out-of-Band AP 이미지 다운로드 기능에서 발견된 파일 업로드 취약점 (CVSS: 10.0)

> 영향받는 시스템에 JSON Web Token(JWT)이 하드 코딩되어 있어 이를 악용해 임의 파일을 업로드하여 추가 악성 행위를 수행할 수 있음

 

- JSON Web Token(JWT) [3][4][5]

> JSON 객체에 인증에 필요한 정보들을 담은 후 비밀키로 서명한 토큰으로, 인터넷 표준 인증 방식

 

- Out-of-Band AP 이미지 다운로드 기능 [6]

> 새로운 AP가 컨트롤러에 연결될 때, 해당 AP 운영에 필요한 이미지를 CAPWAP 프로토콜을 사용해 전송

> 네트워크 제약 등 다양한 제약 사항으로 인해 CAPWAP가 아닌 HTTPS를 사용해 이미지를 다운로드할 수 있도록 하는 기능

> 기본적으로 비활성화되어 있음

※ CAPWAP (Control and Provisioning of Wireless Access Points) : 무선 액세스 포인트(AP)와 무선 LAN 컨트롤러(WLC) 간 통신을 위해 사용되는 프로토콜

영향받는 버전
- 클라우드용 Catalyst 9800-CL 무선 컨트롤러
- Catalyst 9300, 9400 및 9500 시리즈 스위치용 Catalyst 9800 임베디드 무선 컨트롤러
- Catalyst 9800 시리즈 무선 컨트롤러
- Catalyst AP에 내장된 무선 컨트롤러
Out-of-Band AP 이미지 다운로드 기능이 Default로 비활성화이므로 해당 기능을 사용 중인 경우만 취약점에 영향받음

3. 대응방안

- 벤더사 제공 업데이트 적용 [7]

> 즉각적인 업데이트가 불가할 경우 Out-of-Band AP 이미지 다운로드 기능 비활성화

[Out-of-Band AP 이미지 다운로드 기능 활성화 여부 확인 방법]
> "show running-config | include ap upgrade" 명령의 결과가 아래처럼 나올 경우 해당 기능 활성화

wlc# show running-config | include ap
upgrade ap upgrade method https
wlc#

4. 참고

[1] https://www.cisco.com/site/us/en/products/networking/wireless/wireless-lan-controllers/index.html
[2] https://nvd.nist.gov/vuln/detail/CVE-2025-20188
[3] https://velog.io/@chuu1019/%EC%95%8C%EA%B3%A0-%EC%93%B0%EC%9E%90-JWTJson-Web-Token
[4] https://puleugo.tistory.com/138
[5] https://blog.bizspring.co.kr/%ED%85%8C%ED%81%AC/jwt-json-web-token-%EA%B5%AC%EC%A1%B0-%EC%82%AC%EC%9A%A9/
[6] https://www.cisco.com/c/en/us/td/docs/wireless/controller/9800/17-13/config-guide/b_wl_17_13_cg/m_eff_image_upgrade_ewlc.html
[7] https://sec.cloudapps.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-wlc-file-uplpd-rHZG9UfC
[8] https://ismailtasdelen.medium.com/ciscos-ios-xe-nightmare-how-a-hard-coded-jwt-in-cve-2025-20188-let-hackers-go-full-root-%EF%B8%8F-%EF%B8%8F-48dc088a8bdb
[9] https://thehackernews.com/2025/05/cisco-patches-cve-2025-20188-100-cvss.html
[10] https://www.bleepingcomputer.com/news/security/cisco-fixes-max-severity-ios-xe-flaw-letting-attackers-hijack-devices/

1. SAP NetWeaver Visual Composer

- SAP NetWeaver : SAP의 애플리케이션 통합 및 실행 플랫폼으로, 다양한 SAP 모듈과 시스템 간 연결을 지원 [1]
- SAP NetWeaver Visual Composer : NetWeaver 상에서 동작하는 시각적 UI 개발 도구로, 코드 없이 SAP 비즈니스 앱의 화면을 설계할 수 있음 [2]

2. CVE-2025-31324

[사진 1] CVE-2025-31324 [3]

- Metadata Uploader 컴포넌트에서 접근 제어가 제대로 이루어지지 않아 임의의 파일 업로드가 가능한 취약점 (CVSS: 10.0)

> /developmentserver/metadatauploader 엔드포인트에서 접근 제어가 제대로 이루어지지 않아, 공격자가 인증 없이 JSP 웹셸 파일을 서버에 업로드 가능

> SAP Visual Composer는 기본 설치 항목은 아니지만, 다수의 시스템에서 활성화되어 있음

> 공격자가 JSP 웹쉘을 서버의 퍼블릭 디렉터리에 업로드해 인증 없이 원격 제어하는 등 활발히 악용 중이므로, 긴급 패치 권고

 -영향받는 버전
SAP NetWeaver VCFRAMEWORK 7.50

[사진 2] 실제 공격에 악용된 HTTP POST 요청 [4]

2.1 취약점 스캐너

- 보안 기업 Onapsis는 취약점을 확인할 수 있는 스캐너를 제공 [5]

> GitHub에서 스캐너의 최신 버전을 확인한 후 지정한 SAP 서버의 취약점 여부 확인

① 대상 SAP 서버의 /developmentserver/metadatauploader URL로 HEAD 요청 전송

 ⒜ 200 응답Set-Cookie 헤더가 없는 경우 취약

 ⒝ 404 응답 또는 다른 응답의 경우 취약하지 않음

사전 정의된 웹쉘 목록(KNOWN_WEBSHELLS)경로(/irj)를 대상으로 업로드된 웹쉘 확인

 ⒜ 200 응답일 경우 웹쉘 존재

※ 사전 정의된 웹쉘 목록과 특정 경로만을 대상으로 스캔을 수행하므로 정의되지 않은 웹쉘명과 경로에대한 검증은 불가

import requests
import argparse
import json
from packaging.version import parse as parse_version

__version__ = "1.0.2"
KNOWN_WEBSHELLS = ["cache.jsp", "helper.jsp"]
GITHUB_REPO = (
    "Onapsis/Onapsis_CVE-2025-31324_Scanner_Tools"
)


def check_cve_2025_31324(hostname, port, use_ssl):
    protocol = "https" if use_ssl else "http"
    url = f"{protocol}://{hostname}:{port}/developmentserver/metadatauploader"

    try:
        response = requests.head(url, timeout=10, verify=False)
        status_code = response.status_code
        if status_code == 200 and 'Set-Cookie' not in response.headers:
            print(
                f"[CRITICAL] SAP System at {url} appears to be vulnerable to "
                "CVE-2025-31324."
            )
        elif status_code == 404:
            print(
                f"[INFO] Visual Composer SAP System at {url} appears to not "
                "be installed or unavailable."
            )
        else:
            print(
                f"[INFO] The SAP system at {url} does not appear to be "
                "vulnerable to CVE-2025-31324."
            )
    except requests.exceptions.RequestException as e:
        print(f"Error connecting to {url} for vulnerability testing: {e}")


def test_webshell(hostname, port, use_ssl):
    webshell_found = False
    for webshell_filename in KNOWN_WEBSHELLS:
        protocol = "https" if use_ssl else "http"
        url = f"{protocol}://{hostname}:{port}/irj/{webshell_filename}"
        try:
            response = requests.get(url, timeout=10, verify=False)
            if response.status_code == 200:
                print(f"[CRITICAL] Known webshell found at: {url}")
                webshell_found = True

        except requests.exceptions.RequestException as e:
            print(
                f"[ERROR] Error connecting to {url} for webshell testing: {e}"
            )
    if not webshell_found:
        print("[INFO] No known webshells found.")


def check_for_updates():
    try:
        url = f"https://api.github.com/repos/{GITHUB_REPO}/releases/latest"
        response = requests.get(url)
        response.raise_for_status()
        release_info = response.json()
        latest_version = release_info.get("tag_name")
        if latest_version:
            latest_version = latest_version.lstrip("v")
            current_version = parse_version(__version__)
            latest_parsed_version = parse_version(latest_version)
            if latest_parsed_version > current_version:
                print(f"[WARNING] There is a newer version, {latest_version}.")
                print(f"You are currently using version {__version__}.")

        else:
            print("Could not retrieve the latest release information.")
    except requests.exceptions.RequestException as e:
        print(f"Error checking for updates: {e}")
    except json.JSONDecodeError:
        print("Error decoding release information.")


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description=(
            "Onapsis Scanner for Vulnerability CVE-2025-31324 (SAP Security "
            "3594142) - CVSS 10 (Critical). This tool checks for the presence "
            "of the vulnerability and known webshells in the SAP system. \n\n"
            "DISCLAIMER: This tool is provided from Onapsis via open source "
            "license Apache 2.0, as a contribution to the security, incident "
            "response, and SAP communities to aid in response to active "
            "exploitation of CVE-2025-31324. This tool is under development "
            "and will continue to iterate rapidly as more information becomes "
            "available either from Onapsis Research Labs or publicly. "
            "This is a best-effort development and offered as-is with no "
            "warranty or liability."
        )
    )
    parser.add_argument(
        "hostname",
        help=(
            "Hostname or IP address of the SAP system."
        )
    )
    parser.add_argument(
        "port",
        type=int,
        help="Port number of the SAP system (i.e. 50000)."
    )
    parser.add_argument(
        "--ssl",
        action="store_true",
        help="Use SSL/TLS for the connection."
    )

    args = parser.parse_args()
    check_for_updates()
    check_cve_2025_31324(args.hostname, args.port, args.ssl)
    test_webshell(args.hostname, args.port, args.ssl)

3. 대응방안

- 벤더사 제공 업데이트 적용 [6][7][8]

제품명 영향받는 버전 해결 버전
SAP NetWeaver VCFRAMEWORK 7.50 별도 보안 패치 제공 [6][7][8]

 

- 침해 여부 확인 방법 [9]

> 다음 OS 디렉토리의 루트에 'jsp', 'java', 'class' 파일 존재 여부 확인

① C:\usr\sap\<SID>\<InstanceID>\j2ee\cluster\apps\sap.com\irj\servlet_jsp\irj\root
② C:\usr\sap\<SID>\<InstanceID>\j2ee\cluster\apps\sap.com\irj\servlet_jsp\irj\work
③ C:\usr\sap\<SID>\<InstanceID>\j2ee\cluster\apps\sap.com\irj\servlet_jsp\irj\work\sync

[예시]
[root@sapserver irj]# pwd
/usr/sap/<SID>/<INSTANCE>/j2ee/cluster/apps/sap.com/irj/servlet_jsp/irj
[root@sapserver irj]# find . -type f -name “*.jsp” -ls
[root@sapserver irj]# find . -type f -name “*.java” -ls
[root@sapserver irj]# find . -type f -name “*.class” -ls

 

> SAP 악용에 사용된 웹쉘 및 악용 IP IOC 제공 [9]

① 몇 가지 예외를 제외하고 대부분 파일명은 "무작위 8자리.JSP" 형태

구분 SHA256
Helper.jsp 1f72bd2643995fab4ecf7150b6367fa1b3fab17afd2abed30a98f075e4913087
Cache.jsp 794cb0a92f51e1387a6b316b8b5ff83d33a51ecf9bf7cc8e88a619ecb64f1dcf
Random 8-character names ([a-z]{8}).jsp  b3e4c4018f2d18ec93a62f59b5f7341321aff70d08812a4839b762ad3ade74ee

 

 다음 디렉토리 내에 .jsp, .class, .java 확장자 파일은 악성으로 간주

⒜ /usr/sap/<SID>/<InstanceID>/j2ee/cluster/apps/sap.com/irj/servlet_jsp/irj/root
⒝ /usr/sap/<SID>/<InstanceID>/j2ee/cluster/apps/sap.com/irj/servlet_jsp/irj/work
⒞ /usr/sap/<SID>/<InstanceID>/j2ee/cluster/apps/sap.com/irj/servlet_jsp/irj/work/sync

 

- 탐지 규칙

[YARA]
rule detect_CVE202531324_webshells_by_name

{
    meta:
        description = “Detects the known webshell file names that are uploaded in the root directory”
        author = “Emanuela Ionas, Onapsis Research Labs”
        date = “2025-04-30”
        tags = “CVE-2025-31324”
    strings:
        $path_1 = “/irj/root/”
        $path_2 = “/irj/”

        $webshell_1 = “cache.jsp” nocase
        $webshell_2 = “helper.jsp” nocase
        $webshell_4 = “[a-zA-Z0-9]{8}\.jsp”

        $status = “HTTP/[1,2]\.[0,1,2] 200”
    condition:
        ($webshell_1 or $webshell_2 or $webshell_4) and ($path_1 or $path_2) and $status
}

[SNORT]

alert tcp any any -> any any (msg:"CVE-2025-31324";flow:to_server,established;content:"POST";content:"/developmentserver/metadatauploader";content:"multipart/form-data";content:"filename="; content:".jsp";nocase;)

4. 참고

[1] https://help.sap.com/docs/SAP_NETWEAVER_702/ff55ab4f6c5510149ce7df0d5dc0da07/4a24dbfa64550455e10000000a421937.html
[2] https://help.sap.com/docs/SAP_NETWEAVER_702/ff55ab4f6c5510149ce7df0d5dc0da07/48db676f63f45c97e10000000a42189d.html
[3] https://nvd.nist.gov/vuln/detail/CVE-2025-31324
[4] https://www.picussecurity.com/resource/blog/cve-2025-31324-sap-netweaver-remote-code-execution
[5] https://github.com/Onapsis/Onapsis_CVE-2025-31324_Scanner_Tools
[6] https://support.sap.com/en/my-support/knowledge-base/security-notes-news/april-2025.html
[7] https://accounts.sap.com/saml2/idp/sso
[8] https://www.boho.or.kr/kr/bbs/view.do?bbsId=B0000133&pageIndex=1&nttId=71729&menuNo=205020
[9] https://onapsis.com/blog/active-exploitation-of-sap-vulnerability-cve-2025-31324/
[10] https://www.rapid7.com/blog/post/2025/04/28/etr-active-exploitation-of-sap-netweaver-visual-composer-cve-2025-31324
[11] https://reliaquest.com/blog/threat-spotlight-reliaquest-uncovers-vulnerability-behind-sap-netweaver-compromise
[12] https://www.dailysecu.com/news/articleView.html?idxno=165680

1. Fancy Product Designer

- 온라인에서 의류, 머그컵, 휴대폰 케이스 등을 사용자 맞춤형으로 디자인할 수 있게 해주는 Worpress 플러그인 [2]

2. 취약점

2.1 CVE-2024-51818 [3]

- 취약점은 class-wc-dokan.php의 get_products_sql_attrs() 함수에 존재
> 해당 함수는 class-product.php의 get_products()에 의해 호출
> Line13 : $attrs를 매개변수로 fpd_get_products_sql_attrs() 호출

 

- get_products_sql_attrs()
> Line23 : fpd_filter_users_select 값이 존재하고, -1이 아닌 경우 if문 실행
> Line24 : "user_id=" 문자열 뒤 strip_tags($_POST['fpd_filter_users_select'])를 추가한 결과를 $where 변수에 할당

 

strip_tags()는 NULL bytes와 HTML 및 PHP 태그를 제거하는 함수로 SQL 공격을 방지하지 못함 [4]
> Line29~31 : $where 값은 get_products()의 $wpdb->get_results로 쿼리에 실행

[사진 1] strip_tags()

inc/api/class-product.php, function get_products()
1     public static function get_products( $attrs = array(), $type = 'catalog' ) {
2     
3     global $wpdb;
4     
5     $defaults = array(
6     'cols' => '*',
7     'where' => '',
8     'order_by' => '',
9     'limit' => null,
10     'offset' => null
11     );
12     
13     $attrs = apply_filters( 'fpd_get_products_sql_attrs', $attrs );
14     
15     extract( array_merge( $defaults, $attrs ) );
16     
17     $products = array();
18     if( fpd_table_exists(FPD_PRODUCTS_TABLE) ) {
19     
20     $where = empty($where) ? $wpdb->prepare( 'WHERE type="%s"', $type) : $wpdb->prepare( 'WHERE type="%s" AND ', $type ) . $where;
21     
22     if( !preg_match('/^[a-zA-Z]+\\s(ASC|DESC)$/', $order_by) )
23     $order_by = '';
24     $order_by = empty($order_by) ? '' : 'ORDER BY '. $order_by;
25     
26     $limit = empty($limit) ? '' : $wpdb->prepare( 'LIMIT %d', $limit );
27     $offset = empty($offset) ? '' : $wpdb->prepare( 'OFFSET %d', $offset );
28     
29     $products = $wpdb->get_results(
30     SELECT $cols FROM .FPD_PRODUCTS_TABLE." $where $order_by $limit $offset"
31     );
32     
33     }
34     
35     return $products;
36     
37     }

woo/class-wc-dokan.php, function get_products_sql_attrs
1     public function get_products_sql_attrs( $attrs ) {
2     
3     $where = isset( $attrs['where'] ) ? $attrs['where'] : null;
4     
5     if( self::user_is_vendor() ) {
6     
7     $user_ids = array(get_current_user_id());
8     
9     //add fpd products from user
10     $fpd_products_user_id = fpd_get_option( 'fpd_wc_dokan_user_global_products' );
11     
12     //skip if no use is set or on product builder
13     if( $fpd_products_user_id !== 'none' && !(isset( $_GET['page'] ) && $_GET['page'] === 'fpd_product_builder') )
14     array_push( $user_ids, $fpd_products_user_id );
15     
16     $user_ids = join( ",", $user_ids );
17     
18     $where = empty($where) ? "user_id IN ($user_ids)" : $where." AND user_id IN ($user_ids)";
19     
20     }
21     
22     //manage products filter
23     if( isset($_POST['fpd_filter_users_select']) && $_POST['fpd_filter_users_select'] != "-1" ) {
24     $where = "user_id=".strip_tags( $_POST['fpd_filter_users_select'] );
25     
26     
27     $attrs['where'] = $where;
28     
29     return $attrs;
30     
31     }

 

2.2 CVE-2024-51919 [5]

- 취약점은 class-pro-export.php의 save_remote_file() 함수와 fpd-admin-functions.php의 fpd_admin_copy_file() 함수에 존재

 

- save_remote_file()
> Line9 : $remote_file_url을 통해 원격 URL 값을 받아 fpd_admin_copy_file() 호출

 

- fpd_admin_copy_file()
> Line8 : basename($file_url)의 결과를 $filename에 할당
> Line10 ~ Line22 : 파일을 복사 또는 저장
파일에 대한 검사 없이 복사 또는 저장하므로 임의의 파일 업로드가 가능

pro-export/class-pro-export.php, function save_remote_file()
1     public static function save_remote_file( $remote_file_url ) {
2     
3         $unique_dir = time().bin2hex(random_bytes(16));
4         $temp_dir = FPD_ORDER_DIR . 'print_ready_files/' . $unique_dir;
5         mkdir($temp_dir);
6     
7         $local_file_path = $temp_dir;
8     
9         $filename = fpd_admin_copy_file(
10             $remote_file_url,
11             $local_file_path
12         );
13     
14         return $filename ? $unique_dir . '/' . $filename : null;
15     
16     }

admin/fpd-admin-functions.php, function fpd_admin_copy_file()
1     function fpd_admin_copy_file( $file_url, $destination_dir ) {
2     
3     if( empty( $file_url ) ) return false;
4     
5     if( !file_exists($destination_dir) )
6             wp_mkdir_p( $destination_dir );
7     
8     $filename = basename( $file_url );
9     
10     if( function_exists('copy') ) {
11     
12     return copy( $file_url, $destination_dir . '/' . $filename ) ? $filename : false;
13     
14     }
15     else {
16     
17     $content = file_get_contents( $file_url );
18     $fp = fopen( $destination_dir . '/' . $filename, 'w' );
19     $bytes = fwrite( $fp, $content );
20     fclose( $fp );
21     
22     return $bytes !== false ? $filename : false;
23     
24     }

3. 대응방안

- 취약점이 벤더사에 전달 되었으나, 최근 버전(6.4.3)까지 패치가 이루어지지 않은 상태
> 권고사항
ⓐ임의 파일 업로드 방지 : 안전한 파일 확장자만 허용하는 허용 목록(allowlist) 설정
ⓑ SQL 인젝션 대응 : 데이터베이스 쿼리의 철저한 입력 값 검증 및 적절한 이스케이프 처리
ⓒ 정기적인 보안 점검 : 플러그인 업데이트 상태 주기적 확인 및 새로운 취약점 발생 여부 모니터링
ⓓ 대안 플러그인 고려 : 개발사가 문제를 해결하지 않는 상황에서 보안이 보장된 대안 플러그인을 사용 고려

4. 참고

[1] https://patchstack.com/articles/critical-vulnerabilities-found-in-fancy-product-designer-plugin/
[2] https://fancyproductdesigner.com/
[3] https://patchstack.com/database/wordpress/plugin/fancy-product-designer/vulnerability/wordpress-fancy-product-designer-plugin-6-4-3-unauthenticated-sql-injection-vulnerability
[4] https://www.php.net/manual/en/function.strip-tags.php
[5] https://patchstack.com/database/wordpress/plugin/fancy-product-designer/vulnerability/wordpress-fancy-product-designer-plugin-6-4-3-unauthenticated-arbitrary-file-upload-vulnerability
[6] https://www.dailysecu.com/news/articleView.html?idxno=162891

1. WPLMS 플러그인 (WordPress Learning Management System)

- WordPress를 사용해 LMS를 구축할 수 있도록 돕는 플러그인

※ Learning Management System : 학습 관리 시스템, 온라인으로 학생들의 학습을 관리할 수 있게 해주는 소프트웨어

2. 취약점

2.1 CVE-2024-56046 [2][3]

[사진 1] CVE-2024-56046

- WPLMS에서 발생하는 파일 업로드 취약점 (CVSS: 10.0)

영향받는 버전 : WPLMS <= 1.9.9

 

- includes/vibe-shortcodes/shortcodes.php의 wplms_form_uploader_plupload()에 취약점 존재
> Line9 : $_REQUEST["name"] 값을 우선적으로 $fileName에 할당하며, 해당 값이 없을 경우 $_FILES["file"]["name"] 값을 사용
> Line17 : $fileName은 파일 저장 경로를 결정하는데 사용됨

 

- name 파라미터는 사용자 요청으로부터 추출 (Line9)
> 해당 값에 대한 검증 없이 사용하여 악의적인 파일(Ex. "../../../attack.php")을 사용해 파일을 업로드할 수 있음

 

- $fileName을 기반으로 서버의 특정 경로에 저장
> 해당 값에 대한 검증이 없어 임의 디렉터리에 악의적인 파일을 업로드할 수 있음

includes/vibe-shortcodes/shortcodes.php, function wplms_form_uploader_plupload()
1     function wplms_form_uploader_plupload(){
2       check_ajax_referer('wplms_form_uploader_plupload');
3     
4       if (empty($_FILES) || $_FILES['file']['error']) {
5           die('{"OK": 0, "info": "Failed to move uploaded file."}');
6       }
7       $chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0;
8       $chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 0;
9       $fileName = isset($_REQUEST["name"]) ? $_REQUEST["name"] : $_FILES["file"]["name"];
10     
11       $upload_dir_base = wp_upload_dir();
12       $folderPath = $upload_dir_base['basedir']."/wplms_form_uploader";
13       if(function_exists('is_dir') && !is_dir($folderPath)){
14           if(function_exists('mkdir')) 
15               mkdir($folderPath, 0755, true) || chmod($folderPath, 0755);
16       }
17       $filePath = $folderPath."/$fileName";
18     
19       // Open temp file
20       if($chunk == 0) 
21           $perm = "wb" ;
22       else 
23           $perm = "ab";
24     
25       $out = @fopen("{$filePath}.part",$perm );
26     
27       if ($out) {
28         // Read binary input stream and append it to temp file
29         $in = @fopen($_FILES['file']['tmp_name'], "rb");
30         
31         if ($in) {
32           while ($buff = fread($in, 4096))
33             fwrite($out, $buff);
34         } else
35           die('{"OK": 0, "info": "Failed to open input stream."}');
36         
37         @fclose($in);
38         @fclose($out);
39         
40         @unlink($_FILES['file']['tmp_name']);
41       } else
42         die('{"OK": 0, "info": "Failed to open output stream."}');
43     
44       // Check if file has been uploaded
45       if (!$chunks || $chunk == $chunks - 1) {
46         // Strip the temp .part suffix off
47           rename("{$filePath}.part", $filePath);
48           
49       }
50       die('{"OK": 1, "info": "Upload successful."}');
51       exit;
52     }

 

2.2 CVE-2024-56050 [4][5]

[사진 2] CVE-2024-56050

- WPLMS에서 발생하는 파일 업로드 취약점 (CVSS: 9.9)

영향받는 버전 : WPLMS < 1.9.9.5.3

 

- includes/vibe-shortcodes/upload_handler.php의 wp_ajax_zip_upload()에 취약점 존재
> Line4 ~ Line8 : 사용자 요청에서 값을 추출해 변수 할당
> Line18 ~ Line19 : Zip 파일 내 다른 파일이 있는 경우 extractZip()을 통해 파일 내 모든 내용을 추출
> 사용자 요청에서 추출한 값을 검증없이 사용하여 취약점 발생

 

extractZip()
> Line6 : extractTo()를 사용해 Zip 파일내 모든 파일을 $target 디렉터리에 추출
파일에 대한 검증없이 추출되어 취약점 발생
> attack.php 등의 악의적 파일을 포함한 Zip 파일을 업로드할 수 있는 문제 발생

includes/vibe-shortcodes/upload_handler.php, function wp_ajax_zip_upload()
1     function wp_ajax_zip_upload(){
2     $arr = array();
3     
4     $file = $_FILES['uploadedfile']['tmp_name'];
5     $dir = explode(".",$_FILES['uploadedfile']['name']);
6     $dir[0] = str_replace(" ","_",$dir[0]);
7     $target = $this->getUploadsPath().$dir[0];
8     $index = count($dir) -1;
9     
10     if (!isset($dir[$index]) || $dir[$index] != "zip")
11     $arr[0] = __('The Upload file must be zip archive','wplms');
12     else{
13     while(file_exists($target)){
14     $r = rand(1,10);
15     $target .= $r;
16     $dir[0] .= $r;
17     }
18     if (!empty($file))
19     $arr = $this->extractZip($file,$target,$dir[0]);
20     else
21     $arr[0] = __('File too big','wplms');
22     }
23     echo json_encode($arr);
24     die();
25     }

includes/vibe-shortcodes/upload_handler.php, function extractZip()
1     function extractZip($fileName,$target,$dir){
2     $arr = array();
3     $zip = new ZipArchive;
4     $res = $zip->open($fileName);
5     if ($res === TRUE) {
6     $zip->extractTo($target);
7     $zip->close();
8     $file = $this->getFile($target);
9     ;
10     if($file){
11     $arr[0] = 'uploaded'; 
12     $arr[1] = $this->getUploadsUrl().$dir."/".$file; 
13     $arr[2] = $dir;
14     $arr[3] =$file;
15     $arr[4] = $this->getUploadsPath().$dir; 
16     }else{
17     $arr[0] = __('Please upload zip file, Index.html file not found in package','wplms').$target.print_r($file);
18     $this->rrmdir($target);
19     }
20     }else{
21     $arr[0] = __('Upload failed !','wplms');;
22     }
23     return  $arr;
24     }

 

2.3 CVE-2024-56052 [6][7]

[사진 3] CVE-2024-56052

- WPLMS에서 발생하는 파일 업로드 취약점 (CVSS: 9.9)

영향받는 버전 : WPLMS < 1.9.9.5.3

 

- includes/assignments/assignments.php의 wplms_assignment_plupload()에 취약점 존재
> Line2 ~ Line4 : WordPress 내에서 생성된 요청인지와 로그인 유무를 검증
> Line18 : $user_id 및 $assignment_id를 기반으로 $folderPath 생성

 

- $assignment_id에 대한 유효성 검증이 없어 임의 디렉터리에 악의적인 파일을 업로드할 수 있음

includes/assignments/assignments.php, function wplms_assignment_plupload()
1     function wplms_assignment_plupload(){
2       check_ajax_referer('wplms_assignment_plupload');
3       if(!is_user_logged_in())
4           die('user not logged in');
5     
6       $user_id = get_current_user_id();
7       
8       if (empty($_FILES) || $_FILES['file']['error']) {
9         die('{"OK": 0, "info": "Failed to move uploaded file."}');
10       }
11     
12       $chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0;
13       $chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 0;
14       $fileName = isset($_REQUEST["name"]) ? $_REQUEST["name"] : $_FILES["file"]["name"];
15       
16       $upload_dir_base = wp_upload_dir();
17       $assignment_id = $_POST['assignment_id'];
18       $folderPath = $upload_dir_base['basedir']."/wplms_assignments_folder/".$user_id.'/'.$assignment_id;
19       if(function_exists('is_dir') && !is_dir($folderPath)){
20           if(function_exists('mkdir')) 
21               mkdir($folderPath, 0755, true) || chmod($folderPath, 0755);
22       }
23     
24     
25       $filePath = $folderPath."/$fileName";
26         /*if(function_exists('file_exists') && file_exists($filePath)){
27           echo __(' Chunks upload error ','wplms'). $fileName.__(' already exists.Please rename your file and try again ','wplms');
28           die();
29         }*/
30       // Open temp file
31       if($chunk == 0) $perm = "wb" ;
32       else $perm = "ab";
33     
34       $out = @fopen("{$filePath}.part",$perm );
35     
36       if ($out) {
37         // Read binary input stream and append it to temp file
38         $in = @fopen($_FILES['file']['tmp_name'], "rb");
39         
40         if ($in) {
41           while ($buff = fread($in, 4096))
42             fwrite($out, $buff);
43         } else
44           die('{"OK": 0, "info": "Failed to open input stream."}');
45         
46         @fclose($in);
47         @fclose($out);
48         
49         @unlink($_FILES['file']['tmp_name']);
50       } else
51         die('{"OK": 0, "info": "Failed to open output stream."}');
52         
53         
54       // Check if file has been uploaded
55       if (!$chunks || $chunk == $chunks - 1) {
56         // Strip the temp .part suffix off
57           rename("{$filePath}.part", $filePath);
58           
59       }
60       die('{"OK": 1, "info": "Upload successful."}');
61       exit;
62     }

3. 대응방안

- 벤더사 제공 업데이트 적용 [8][9]
> WPLMS Plugin 1.9.9.5.3

> 파일 이름과 유형을 확인하여 업로드할 수 있는 파일을 제한하는 패치 적용
> 영향을 받는 기능에 대한 추가 권한 확인을 구현하거나 영향을 받는 코드 제거

4. 참고

[1] https://patchstack.com/articles/multiple-critical-vulnerabilities-patched-in-wplms-and-vibebp-plugins/
[2] https://nvd.nist.gov/vuln/detail/CVE-2024-56046
[3] https://patchstack.com/database/wordpress/plugin/wplms-plugin/vulnerability/wordpress-wplms-plugin-1-9-9-unauthenticated-arbitrary-file-upload-vulnerability
[4] https://nvd.nist.gov/vuln/detail/CVE-2024-56050
[5] https://patchstack.com/database/wordpress/plugin/wplms-plugin/vulnerability/wordpress-wplms-plugin-1-9-9-5-3-subscriber-arbitrary-file-upload-vulnerability
[6] https://nvd.nist.gov/vuln/detail/CVE-2024-56052
[7] https://patchstack.com/database/wordpress/plugin/wplms-plugin/vulnerability/wordpress-wplms-plugin-1-9-9-5-2-student-arbitrary-file-upload-vulnerability
[8] https://wplms.io/support/knowledge-base/vibebp-1-9-9-7-7-wplms-plugin-1-9-9-5-2/
[9] https://asec.ahnlab.com/ko/85311/

침해 사고 정보
일자 2021/06/05 ~ 2021/06/11
침해 정보 - 환자정보: 병원등록번호, 성명, 생년월일, 성별, 나이, 진료과, 진단명, 검사일, 검사명, 검사결과
- 직원정보: 사번, 성명, 주민번호, 거주지연락처, 연락처, 이메일, 근무부서정보, 직급연차정보, 임용퇴직정보, 휴복직정보, 자격면허정보 등
특징 파일 업로드 취약점을 이용한 웹쉘 업로드로
피해크기 - 약 83만명의 개인정보
> 환자 81만여명
> 전·현직 직원 1만7000여명 
침해 사고 분석
경위 21.05 ~ 21.06 국내·외에 소재한 서버 7대를 장악해 공격 기반 마련
> 국내 4대·해외 3대

② 웹쉘 업로드
> 내부망에서 사용하기 위한 계정 생성
> ID/PW : default/다치지 말라

③ 공유폴더와 연결된 서울대병원 내부망에 침입

④ 병리검사 서버에서 개인정보 탈취
> 81만명의 진료정보를 탈취

⑤ 내부망 전자사보DB에서 개인정보 탈취
> 1만 7000여명의 직원정보 탈취
> 이중 2000명의 정보는 실제 유출된 것으로 확인
원인 ① 파일 업로드가 가능한 병원 내부망의 보안 취약점을 활용
> 웹서버에 명령을 실행해 관리자 권한을 획득할 수 있는 웹쉘이 필터링 되지 않고 업로드 됨
조치 ① 21.07.06 서울대병원 침해 사실 최초 인지 및 공지 게시
- 교육부/보건복지부/개인정보보호위원회/사이버수사대 등에 신고후 조사를 진행
- 서울대병원의 후속조치
> 해당 IP 및 접속 경로 차단
> 서비스 분리
> 취약점 점검 및 보완조치
> 모니터링 강화
> 사용자 PC 비밀번호 변경
> 개인정보보호위원회 등 관련 유관기관 신고
> 병원에 등록된 휴대전화번호로 개인정보 유출 사실 개별 연락 수행

② 23.05.10 경찰청 발표
- 피해기관에 침입 및 정보 유출 수법과 재발 방지를 위한 보안 권고사항을 설명
- 관계기관에 북한 해킹조직의 침입 수법·해킹 도구 등 관련 정보를 제공
> 국가 배후의 조직적 사이버 공격에 대해 치안 역량을 총동원하여 적극적으로 대응
> 관계기관 정보공유 및 협업을 통해 추가적인 피해를 방지
> 사이버 안보를 굳건히 지키기 위해 노력할 계획

- 경찰
> 개인정보보호위원회 의결 결과에 따라 병원 개인정보 보호책임자 입건 여부 검토 예정

③ 23.05.10 개인정보보호위원회
- 전체회의를 통해 서울대학교병원에 과징금 7,475만원 부과 의결
> 서울대병원이 이미 널리 알려진 해킹공격(웹쉘)을 탐지하고, 방어할 관리적·기술적 조치가 미비했다고 판단
> 공공기관 최초로 과징금을 부과
기타 ① 기존 북한발로 확인된 다수 사건과 비교 결과 해당 사건 또한 북한발(김수키, Kimsuky)로 확인
- 기존 사례와 동일한 사항
> 공격 근원지 IP, IP 주소 세탁 기법: 과거 북한 해킹 조직이 사용했던 IP 포함
> 인터넷 사이트 가입정보, 시스템 침입·관리 수법: 해킹용 서버의 사용자 이름·이메일이 과거 북한 해킹조직이 사용한 정보
> 북한어휘 사용: 내부망에 생성한 계정의 비밀번호가 한글 자판으로 '다치지 말라'

② 이승운 경찰청 사이버테러수사대장
- 해킹 조직이 병리검사가 저장됐던 서버를 해킹
- 고위 인사의 개인정보를 빼내기 위한 목적으로 추정

③ 남석 개인정보위 조사조정국장
> 공공기관의 경우 다량의 개인정보를 처리
> 작은 위반행위로도 심각한 피해로 이어질 가능성이 큰 만큼 담당자들의 세심한 주의가 필요

④ 의료 분야 외 다른 분야 또한 주요 정보통신망에 대한 침입 시도가 지속될 것으로 예상
- 최신 보안 업데이트 적용
- 불법적인 접속시도에 대한 접근통제
- 개인정보를 포함한 중요 전산 자료 암호화 등 보안 시스템과 보안정책 강화 권고

 

'침해사고 > 개인정보' 카테고리의 다른 글

유안타증권 개인정보 유출  (0) 2023.07.21
해병대 개인정보 유출  (2) 2023.06.16
인터파크 개인정보 유출  (0) 2023.04.16
한국남부발전 개인정보 유출  (0) 2022.08.31
빗썸 개인정보 유출  (0) 2022.08.24

+ Recent posts