import requests
import sys
import urllib3
# 禁用不安全请求警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def check_cve_2022_35918(target_url):
"""
检测目标 Streamlit 应用是否存在 CVE-2022-35918 路径遍历漏洞
:param target_url: 目标 Streamlit 应用的基础 URL (例如: http://localhost:8501)
:return: 检测结果字符串
"""
if not target_url.startswith(('http://', 'https://')):
return "[-] 错误: URL 必须以 http:// 或 https:// 开头"
# 确保 URL 末尾没有斜杠
base_url = target_url.rstrip('/')
# 构造测试 payload
# 该漏洞通常涉及对静态资源路径的遍历尝试
# 常见的测试路径是尝试访问 /static/ 之外的文件
# 注意:实际漏洞利用可能涉及更复杂的路径构造,此处仅做基础连通性和版本指纹检查建议
print(f"[*] 正在检测目标: {base_url}")
try:
# 1. 检查 Streamlit 是否正在运行
resp = requests.get(f"{base_url}/", timeout=5, verify=False)
if resp.status_code != 200:
return f"[-] 目标不可达或返回状态码: {resp.status_code}"
# 2. 尝试获取版本信息 (如果暴露)
# Streamlit 通常在页面源码或特定接口中泄露版本
# 这里我们主要依赖版本比对作为最安全的判断方式
# 3. 模拟漏洞检测逻辑 (非破坏性)
# 真正的 PoC 会尝试读取 /etc/passwd 或类似文件
# 为了安全,我们只检查是否存在已修复版本的特征或已知漏洞版本的特征
# 由于直接请求敏感文件可能被 WAF 拦截或产生日志,
# 最佳实践是检查 Server 头或页面中的版本标识
version = extract_version(resp.text)
if version:
print(f"[*] 检测到 Streamlit 版本: {version}")
if is_vulnerable(version):
return f"[!] 危险: 版本 {version} 存在 CVE-2022-35918 漏洞,建议立即升级到 1.11.1 或更高版本"
else:
return f"[+] 安全: 版本 {version} 已修复该漏洞或不受影响"
else:
# 如果无法获取版本,尝试发送一个无害的探测请求
# 注意:以下路径仅为示例,实际漏洞利用路径可能不同
probe_url = f"{base_url}/static/../README.md"
resp_probe = requests.get(probe_url, timeout=5, verify=False, allow_redirects=False)
# 如果返回 200 且内容包含 README 特征,可能存在遍历
if resp_probe.status_code == 200 and "readme" in resp_probe.text.lower():
return "[!] 危险: 疑似存在路径遍历漏洞,请手动核查并升级"
else:
return "[?] 未知: 无法确定版本,但未发现明显的简单遍历特征。建议强制升级到最新版。"
except requests.exceptions.RequestException as e:
return f"[-] 请求失败: {str(e)}"
def extract_version(html_content):
"""
从 HTML 内容中提取 Streamlit 版本号
"""
import re
# 尝试匹配常见的版本标识模式
match = re.search(r'Streamlit v(\d+\.\d+\.\d+)', html_content)
if match:
return match.group(1)
return None
def is_vulnerable(version_str):
"""
判断版本是否在受影响范围内 [0.63.0, 1.11.1)
"""
try:
parts = list(map(int, version_str.split('.')))
if len(parts) < 3:
return False
major, minor, patch = parts, parts, parts
# 受影响范围: >= 0.63.0 且 < 1.11.1
if major == 0:
if minor > 63:
return True
elif minor == 63:
return True # 0.63.0 及以上
else:
return False # < 0.63.0
elif major == 1:
if minor < 11:
return True
elif minor == 11:
if patch < 1:
return True
else:
return False # >= 1.11.1
else:
return False # > 1.11.x
else:
return False # > 1.x.x
except Exception:
return False
if __name__ == "__main__":
if len(sys.argv) != 2:
print("用法: python cve_check.py <target_url>")
print("示例: python cve_check.py http://localhost:8501")
sys.exit(1)
target = sys.argv[1]
result = check_cve_2022_35918(target)
print(result)