오브의 빛나는 별

[오늘의 보안 동향] n8n 임의 파일 읽기 취약점(CVE-2026-21858) 본문

오늘의 보안

[오늘의 보안 동향] n8n 임의 파일 읽기 취약점(CVE-2026-21858)

오브의 별 2026. 3. 5. 17:27
반응형

<3~7줄 요약>

2026년 1월 7일 오픈소스 워크플로우 자동화 플랫폼 n8n에서 'Ni8mare'로 알려진 CVE-2026-21858 임의 파일 읽기 취약점이 공개되었으며, 운영 환경에 따라 원격 코드 실행(RCE)으로 이어질 수 있는 치명적인 취약점
취약 버전은 1.65.0 이상 1.121.0 미만이며, 보안 검색 엔진 Censys 분석 결과 전 세계적으로 약 113,052대의 n8n 인스턴스가 활성화 중이고 한국은 9,266대(8.22%)로 전 세계 4위 수준의 높은 사용량 기록
취약점의 핵심은 Form Trigger 요청 처리 시 Content-Type 헤더를 엄격히 검증하지 않아, 공격자가 application/json 형식의 조작된 요청으로 로컬 파일 경로(filepath)를 직접 주입할 수 있다는 점
공격자는 임의 파일 읽기를 통해 n8n의 config 파일, DB 정보, 암호화 키 등 민감한 정보를 탈취한 후 관리자 JWT를 생성하여 원격 코드 실행 노드를 생성하고 시스템 명령을 실행 가능
n8n 개발팀은 2025년 11월 18일 보안 패치(1.121.0 이상)를 공개했으며, 요청의 Content-Type이 실제로 multipart/form-data 형식인지 확인하는 검증 로직을 추가하여 근본적으로 해결
패치 적용이 어려운 경우 불필요한 폼 트리거 워크플로우 비활성화, 노드별 Basic Auth/Header 인증 적용, 진단 스크립트를 통한 취약 여부 자동 진단 등의 보완 조치 필요

 

<용어 및 프로그램 설명>

n8n(엔에이튼) : 오픈소스 워크플로우 자동화 플랫폼으로, 드래그 앤 드롭 방식으로 비프로그래머도 복잡한 업무 자동화를 구성할 수 있으며, 셀프 호스팅이 가능해 개인부터 기업까지 폭넓게 사용됨

Webhook(웹훅) : 외부 시스템에서 서버로 특정 데이터를 실시간으로 보내주는 HTTP 기반의 자동 호출 메커니즘으로, 일반 API 폴링 방식보다 즉각적인 데이터 전달이 가능함

Form Trigger(폼 트리거) : n8n이 제공하는 기능으로, 웹 인터페이스를 통해 사용자가 파일을 업로드할 수 있게 해주는 폼 기반 입력 엔드포인트

Content-Type : HTTP 요청 헤더로, 요청 본문의 미디어 타입을 명시하는 필드. multipart/form-data는 파일 업로드, application/json은 JSON 형식의 데이터를 나타냄

임의 파일 읽기(Arbitrary File Read) : 공격자가 인증 없이 또는 부정한 방식으로 서버의 로컬 파일 시스템에 접근하여 민감한 파일의 내용을 읽어내는 취약점

JWT(JSON Web Token) : 사용자 인증 정보를 안전하게 전달하기 위한 토큰 형식으로, n8n에서 관리자 권한을 획득하기 위해 악용될 수 있음

원격 코드 실행(RCE, Remote Code Execution) : 공격자가 원격으로 대상 시스템에 임의의 명령어를 실행하는 공격으로, 가장 심각한 수준의 보안 위협

multipart/form-data : 파일을 포함한 폼 데이터를 전송할 때 사용하는 Content-Type으로, 요청 본문을 여러 부분으로 나누어 전송함

PoC(Proof of Concept) : 취약점의 실제 동작을 입증하기 위해 만든 개념 증명 코드 또는 스크립트로, 보안 연구자들이 취약점을 검증할 때 사용

Attack Surface(공격 표면) : 공격자가 침투할 수 있는 모든 진입점과 취약한 부분의 총합으로, 이를 최소화하는 것이 보안의 핵심

<출처 및 원본 문서>

문서명: Research & Technique 2월호_n8n 임의 파일 읽기 취약점(CVE-2026-21858).pdf
출처: EQST insight
발행일: 2026년 2월호
관련 GitHub Repository: https://github.com/EQSTLab/CVE-2026-21858

https://www.skshieldus.com/kor/media/newsletter/insight.do

 

인사이트 리포트 | SK쉴더스

고객님들이 캡스에 자주 묻는 질문들, 빠르게 해답을 찾아보세요.

www.skshieldus.com

 

<원문 내용>

서론
2026년 1월 7일, 오픈소스 워크플로우 자동화 플랫폼인 n8n에서, 운영 환경에 따라 원격 코드 실행으로 이어질 수 있는 임의 파일 읽기 취약점(CVE-2026-21858)이 공개되었다. 해당 취약점은 'Ni8mare'라는 별칭으로 알려졌으며, n8n의 Webhook(웹훅) 및 Form(폼) 요청 처리 과정에서 요청 데이터에 대한 검증이 충분히 이뤄지지 않은 데서 발생한다.

n8n은 드래그 앤 드롭 방식으로 워크플로우를 구성할 수 있으며, 셀프 호스팅이 가능하다는 점에서 개인 사용자부터 기업 환경까지 폭넓게 사용되고 있다. 보안 검색 엔진 Censys의 분석 결과, 2026년 2월 기준 전 세계적으로 약 113,052대의 n8n 인스턴스가 활성화되어 있으며, 그중 한국은 약 9,266대(8.22%)로 전 세계 4위의 높은 사용량을 기록하고 있다.

공격자는 폼 기반 워크플로우를 대상으로 조작된 요청을 전송해, 서버가 로컬 파일을 업로드 파일로 오인하도록 유도할 수 있다. 그 결과 서버 내 민감한 파일이 워크플로우 처리 과정에서 외부로 노출될 수 있다. 따라서 n8n을 운영 중인 조직은 사용 중인 버전이 취약한 범위에 해당하는지 신속히 확인하고, 보안 패치를 적용하거나 추가적인 보호 조치를 검토할 필요가 있다.

영향받는 소프트웨어 버전
CVE-2026-21858에 취약한 소프트웨어는 n8n 1.65.0 이상 1.121.0 미만이다.

공격 시나리오
공격 과정은 5단계로 구성된다.

①단계: 공격자는 Form Trigger(폼 트리거) 엔드포인트에 피해자 서버 파일 경로를 가리키도록 조작된 JSON 요청을 전송하여 내부 파일을 유출한다.

②단계: 공격자는 탈취한 내부 파일에서 DB 정보 및 암호화 키로 관리자 JWT를 생성한다.

③단계: 공격자는 생성한 JWT로 REST API 요청을 사용하여 워크플로우와 시스템 명령 실행 노드를 생성한다.

④단계: 공격자는 시스템 명령 실행 노드에 명령어를 설정하고 워크플로우를 실행한다.

⑤단계: 시스템 명령이 실행되고, 공격자는 그 실행 결과를 확인한다.

테스트 환경 구성 정보
테스트 환경은 피해자 환경(ubuntu:22.04 & n8n 1.120.4, IP: 172.17.0.2)과 공격자 환경(Kali Linux, IP: 172.17.0.4)으로 구성되었다.

취약점 테스트
Step 1. 환경 구성
피해자 PC에 n8n 취약 버전을 설치하여 취약 환경을 구성한다. CVE-2026-21858 취약점 테스트 구성을 위한 도커 이미지 및 취약점 테스트 파일은 EQSTLab GitHub Repository에서 확인할 수 있다.

URL: https://github.com/EQSTLab/CVE-2026-21858

로컬 환경에서 피해자 PC와 취약 환경을 구성하기 위해 다음 명령어로 도커 이미지를 빌드한 뒤 실행한다.

git clone https://github.com/EQSTLab/CVE-2026-21858.git
cd CVE-2026-21858
docker build -t n8n-vuln:1.120.4 .
run.bat
n8n 서버가 실행되면, 피해자 PC로 http://localhost:9000/setup에 접근하여 관리자 계정을 생성한다.

관리자 계정으로 워크플로우를 생성하고, workflow.txt의 노드 JSON을 워크플로우에 붙여 넣어 워크플로우를 Active 상태로 전환한다. 해당 워크플로우는 폼 트리거를 통해 외부 사용자가 파일을 업로드하면, 이를 텍스트로 변환하여 응답하는 구조이다.

On form submission 노드를 더블클릭하여 Production URL을 확인한다. 해당 URL의 폼 트리거 페이지에서 파일을 업로드하는 것이 가능하다. 해당 페이지는 앞선 워크플로우에서 활성화할 경우, 외부 사용자들이 사용할 수 있도록 공개되는 엔드포인트이다.

Step 2. 취약점 테스트
공격자 PC에서 취약점 엔트리 포인트인 n8n 폼 트리거 페이지를 확인한다.

PoC 코드를 다운로드하고 실행한다.

git clone https://github.com/EQSTLab/CVE-2026-21858.git
cd CVE-2026-21858
pip3 install -r requirements.txt
python3 poc.py
앞서 확인한 폼 트리거 엔드포인트의 URL을 입력하여 공격을 시도한다.

관리자 JWT를 생성하기 위해 필요한 정보들을 탈취한다. 서버가 디폴트 위치에 중요 정보를 보관하고 있을 경우, 공격자는 파일 경로를 예측해 이를 탈취할 수 있다. 탈취된 정보는 JWT 생성에 사용되며, 공격자는 n8n을 관리자 권한으로 조작할 수 있게 된다.

이후 공격자는 생성한 JWT로 REST API를 호출하고, 워크플로우 및 노드를 생성할 수 있다. 서버의 시스템 명령을 직접 실행할 수 있는 Execute Command 노드를 추가해 n8n 서버에 원격 명령을 수행할 수 있다.

취약점 상세 분석
Ⅰ. 웹훅 개요
웹훅은 외부 시스템과 n8n을 실시간으로 연결하는 HTTP 기반의 자동 호출 메커니즘이다. 외부에서 웹훅 URL로 요청을 전송하면, n8n은 이를 신호로 받아 내부 워크플로우를 즉시 가동한다.

앞선 시나리오의 폼 트리거는 웹훅 메커니즘을 기반으로 동작하는 엔드포인트 중 하나이다. 일반적인 웹훅은 시스템 간 데이터 송수신에 최적화되어 있다. 이와 달리, 폼 트리거는 사용자 입력을 위한 HTML Form 인터페이스를 제공하며 내부적으로는 multipart/form-data 요청을 수신하여 워크플로우를 가동한다. 그러나 요청 처리 시 Content-Type을 엄격히 검증하지 않는 취약점이 존재하여, 서버 내 파일 탈취 위험성이 제기되었다.

Ⅱ. 취약점 상세 분석
취약 버전인 n8n(1.120.4) 코드를 분석하여 어떻게 서버 내 파일이 노출될 수 있었는지 살펴본다.

Step 1. Content-Type에 따른 req.body.files 할당 방식 차이
n8n은 외부 HTTP 요청을 수신하면, 요청의 Content-Type 헤더에 따라 서로 다른 파서로 본문을 처리한다. 이때 파일 업로드 정보로 사용되는 req.body.files가 생성되는 방식이 Content-Type에 따라 달라진다.

정상 요청 - multipart/form-data: 폼 트리거에 정상적으로 폼 데이터를 전송하면 multipart/form-data로 요청이 들어오며, parseFormData()가 호출된다. parseFormData()는 formidable 파서를 통해 업로드 파일을 서버 임시 경로(/tmp/)에 저장하고, 그 경로를 req.body.files['field-0'].filepath로 설정한다. 즉 정상 흐름에서는 filepath가 서버에서 생성한 임시 경로로 고정되므로, 사용자가 임의의 로컬 파일 경로를 지정할 수 없다.

조작된 요청 - application/json: 공격자가 폼 트리거를 통한 요청의 Content-Type을 application/json으로 조작하면, n8n은 parseBody()를 호출하여 본문 JSON을 그대로 req.body로 사용한다. 이 과정에서 본문에 files 구조를 포함하면, req.body.files['field-0'].filepath가 JSON에 포함된 값 그대로 설정된다. 따라서 공격자는 filepath에 서버 로컬 파일 경로를 직접 주입할 수 있다.

Step 2. 트리거 식별 기준과 Content-Type의 불일치
문제는 폼 트리거 요청이 들어오면 n8n은 요청의 Content-Type을 기준으로 이를 판별하지 않는다는 점이다. 대신 요청 URL(/form/)을 기준으로 처리 로직을 결정한다. 즉, 요청 본문 파싱은 Content-Type에 따라 달라지지만, 처리 로직은 Content-Type이 아니라 URL 기반 트리거 식별 결과로 결정된다. 이 구조 때문에 공격자가 application/json으로 req.body.files['field-0'].filepath를 주입해도, 서버는 폼 트리거 요청 처리를 수행한다.

Step 3. filepath 신뢰로 인한 로컬 파일 오인
폼 트리거 요청을 처리할 시, prepareFormReturnItem() 함수로 요청 본문을 returnItem에 저장하고, 이후 워크플로우에서 사용한다. prepareFormReturnItem()은 요청 본문에서 data와 files를 추출하여 폼 트리거 요청 처리 결과(returnItem)를 구성한다. 이 과정에서 files가 multipart/form-data 파서가 생성한 값인지 여부는 확인하지 않는다. 단순히 "폼 트리거로 들어온 요청이라면 multipart/form-data 요청일 것"이라는 전제 하에 처리한다. 따라서 공격자가 application/json 요청으로 주입한 req.body.files['field-0'].filepath도 정상 업로드 파일의 저장 경로로 간주되며, 이후 파일 복사/적재 로직에 그대로 전달될 수 있다.

Step 4. 워크플로우 구성에 따른 파일 노출
위 단계에서 추출된 파일은 returnItem에 복사된다. 이렇게 생성된 returnItem은 이후 워크플로우 동작에서 사용된다. 만약 공격자가 filepath로 서버 내 파일을 가리키는 경우, n8n 서버는 이를 정상적인 업로드 파일로 오인하고 해당 파일을 복사하여 사용한다.

Copy{
  "files": {
    "field-0": {
      "filepath": "/home/n8n/.n8n/config"
    }
  }
}
워크플로우에 업로드 파일을 볼 수 있는 기능이 존재할 경우, 공격자가 지정한 로컬 파일의 내용을 확인할 수 있게 된다.

대응 방안
CVE-2026-21858은 인증 없이 외부 요청만으로 서버 내부 파일에 접근할 수 있는 취약점으로, 실제 운영 환경에 미치는 영향이 매우 크다. 따라서 해당 취약점에 노출된 n8n 인스턴스는 즉각적인 패치 적용을 최우선으로 하되, 패치를 적용하기 어려운 경우를 대비한 추가적인 조치가 필요하다.

① 보안 패치 적용
2025년 11월 18일 n8n 개발팀은 CVE-2026-21858 취약점에 대한 보안 패치를 공개했다. 해당 패치로 폼 데이터를 내부 실행 객체로 복사하기 전, 요청의 Content-Type이 실제로 multipart/form-data 형식인지 여부를 명확히 확인하는 로직이 추가되었다.

packages/nodes-base/nodes/Form/utils/utils.ts 파일의 prepareFormReturnItem() 함수에서 이를 확인할 수 있다. 해당 패치가 적용된 환경에서는 조작된 데이터가 파일 경로 정보로 할당되는 과정 자체가 원천 봉쇄되므로, 본 취약점을 통한 공격은 근본적으로 차단된다.

또한, 웹훅 및 테스트 코드 전반에 Content-Type 검증 로직을 확대 적용하여 유사한 취약점의 재발 가능성을 차단하였다.

패치 버전: n8n 1.121.0 이상

② 보안 패치 적용이 어려운 경우
운영 환경의 제약으로 즉각적인 패치가 어려운 경우, 공격 표면(Attack Surface)을 최소화하기 위해 다음과 같은 조치를 단계적으로 수행해야 한다.

1) 워크플로우 및 노드 관리: 불필요한 폼 트리거 노드가 포함된 워크플로우를 즉시 비활성화해야 한다. 특히 테스트 목적으로 생성된 워크플로우가 운영 환경에 그대로 남아 공격 진입점으로 악용되지 않도록 사전에 비활성화하는 것이 바람직하다.

2) 노드별 인증 메커니즘 강화: 트리거 노드에 Basic Auth 또는 Header 기반 인증을 적용해야 한다. 유효한 인증 정보가 포함되지 않은 외부 HTTP 요청만으로는 내부 워크플로우가 실행되지 않게 대처할 수 있다.

③ 진단 스크립트를 활용한 취약 여부 진단
운영 중인 n8n 인스턴스의 취약 여부를 신속하게 판별하기 위해 자동화된 점검 스크립트를 활용할 수 있다. 본 스크립트는 실제 공격을 수행하는 대신, Content-Type 설정에 따른 서버의 응답 패턴을 분석하여 안전하게 취약점을 진단한다.

진단 원리: n8n의 /form/ 엔드포인트에 application/json 형식의 요청을 보내고, 본문에 filepath 키워드를 포함하여 전송한다. 이때 서버가 요청을 정상적으로 수용할 경우, Content-Type 검증이 적용되지 않은 취약한 상태로 판단한다. 반대로 요청을 거부한다면 패치가 적용된 안전한 상태로 판단한다.

진단 스크립트:
Copyimport requests

def check_n8n_vulnerability():
    url = input("Enter n8n Form URL: ").strip()
    
    if "/form/" not in url:
        print("[NOTICE] Only '/form/' URLs are supported.")
        return
    
    try:
        res = requests.post(
            url,
            json={"filepath": "/etc/passwd", "fileName": "test"},
            headers={'Content-Type': 'application/json'},
            timeout=5
        )
        
        if res.status_code == 200:
            print(f"\n[VULNERABLE] {url} (Status: 200)")
        else:
            print(f"\n[SAFE] {url} (Status: {res.status_code})")
    
    except Exception as e:
        print(f"\n[ERROR] {url}: {e}")

if __name__ == "__main__":
    check_n8n_vulnerability()

상태 메시지 해석
[VULNERABLE]: 해당 인스턴스는 취약하므로 즉시 패치 또는 보완 조치가 필요함
[SAFE]: 보안 패치가 적용되었거나 요청이 정상적으로 차단됨
[NOTICE]: 잘못된 URL 입력

참고 사이트
NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-21858
n8n GitHub Security: https://github.com/n8n-io/n8n/security
n8n Docs: https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.form/
The Hacker News: https://thehackernews.com/2026/01/critical-n8n-vulnerability-cvss-100.html
CYERA: https://www.cyera.com/research-labs/ni8mare-unauthenticated-remote-code-execution-in-n8n-cve-2026-21858
Chocapikk GitHub: https://github.com/Chocapikk/CVE-2026-21858

반응형